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 5package autocert 6 7import ( 8 "context" 9 "crypto/ecdsa" 10 "crypto/elliptic" 11 "crypto/rand" 12 "crypto/tls" 13 "crypto/x509" 14 "encoding/base64" 15 "fmt" 16 "net/http" 17 "net/http/httptest" 18 "testing" 19 "time" 20 21 "golang.org/x/crypto/acme" 22) 23 24func TestRenewalNext(t *testing.T) { 25 now := time.Now() 26 man := &Manager{ 27 RenewBefore: 7 * 24 * time.Hour, 28 nowFunc: func() time.Time { return now }, 29 } 30 defer man.stopRenew() 31 tt := []struct { 32 expiry time.Time 33 min, max time.Duration 34 }{ 35 {now.Add(90 * 24 * time.Hour), 83*24*time.Hour - renewJitter, 83 * 24 * time.Hour}, 36 {now.Add(time.Hour), 0, 1}, 37 {now, 0, 1}, 38 {now.Add(-time.Hour), 0, 1}, 39 } 40 41 dr := &domainRenewal{m: man} 42 for i, test := range tt { 43 next := dr.next(test.expiry) 44 if next < test.min || test.max < next { 45 t.Errorf("%d: next = %v; want between %v and %v", i, next, test.min, test.max) 46 } 47 } 48} 49 50func TestRenewFromCache(t *testing.T) { 51 // ACME CA server stub 52 var ca *httptest.Server 53 ca = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 54 w.Header().Set("Replay-Nonce", "nonce") 55 if r.Method == "HEAD" { 56 // a nonce request 57 return 58 } 59 60 switch r.URL.Path { 61 // discovery 62 case "/": 63 if err := discoTmpl.Execute(w, ca.URL); err != nil { 64 t.Fatalf("discoTmpl: %v", err) 65 } 66 // client key registration 67 case "/new-reg": 68 w.Write([]byte("{}")) 69 // domain authorization 70 case "/new-authz": 71 w.Header().Set("Location", ca.URL+"/authz/1") 72 w.WriteHeader(http.StatusCreated) 73 w.Write([]byte(`{"status": "valid"}`)) 74 // authorization status request done by Manager's revokePendingAuthz. 75 case "/authz/1": 76 w.Write([]byte(`{"status": "valid"}`)) 77 // cert request 78 case "/new-cert": 79 var req struct { 80 CSR string `json:"csr"` 81 } 82 decodePayload(&req, r.Body) 83 b, _ := base64.RawURLEncoding.DecodeString(req.CSR) 84 csr, err := x509.ParseCertificateRequest(b) 85 if err != nil { 86 t.Fatalf("new-cert: CSR: %v", err) 87 } 88 der, err := dummyCert(csr.PublicKey, exampleDomain) 89 if err != nil { 90 t.Fatalf("new-cert: dummyCert: %v", err) 91 } 92 chainUp := fmt.Sprintf("<%s/ca-cert>; rel=up", ca.URL) 93 w.Header().Set("Link", chainUp) 94 w.WriteHeader(http.StatusCreated) 95 w.Write(der) 96 // CA chain cert 97 case "/ca-cert": 98 der, err := dummyCert(nil, "ca") 99 if err != nil { 100 t.Fatalf("ca-cert: dummyCert: %v", err) 101 } 102 w.Write(der) 103 default: 104 t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path) 105 } 106 })) 107 defer ca.Close() 108 109 man := &Manager{ 110 Prompt: AcceptTOS, 111 Cache: newMemCache(t), 112 RenewBefore: 24 * time.Hour, 113 Client: &acme.Client{ 114 DirectoryURL: ca.URL, 115 }, 116 } 117 defer man.stopRenew() 118 119 // cache an almost expired cert 120 key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 121 if err != nil { 122 t.Fatal(err) 123 } 124 now := time.Now() 125 cert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), exampleDomain) 126 if err != nil { 127 t.Fatal(err) 128 } 129 tlscert := &tls.Certificate{PrivateKey: key, Certificate: [][]byte{cert}} 130 if err := man.cachePut(context.Background(), exampleCertKey, tlscert); err != nil { 131 t.Fatal(err) 132 } 133 134 // veriy the renewal happened 135 defer func() { 136 testDidRenewLoop = func(next time.Duration, err error) {} 137 }() 138 done := make(chan struct{}) 139 testDidRenewLoop = func(next time.Duration, err error) { 140 defer close(done) 141 if err != nil { 142 t.Errorf("testDidRenewLoop: %v", err) 143 } 144 // Next should be about 90 days: 145 // dummyCert creates 90days expiry + account for man.RenewBefore. 146 // Previous expiration was within 1 min. 147 future := 88 * 24 * time.Hour 148 if next < future { 149 t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future) 150 } 151 152 // ensure the new cert is cached 153 after := time.Now().Add(future) 154 tlscert, err := man.cacheGet(context.Background(), exampleCertKey) 155 if err != nil { 156 t.Fatalf("man.cacheGet: %v", err) 157 } 158 if !tlscert.Leaf.NotAfter.After(after) { 159 t.Errorf("cache leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after) 160 } 161 162 // verify the old cert is also replaced in memory 163 man.stateMu.Lock() 164 defer man.stateMu.Unlock() 165 s := man.state[exampleCertKey] 166 if s == nil { 167 t.Fatalf("m.state[%q] is nil", exampleCertKey) 168 } 169 tlscert, err = s.tlscert() 170 if err != nil { 171 t.Fatalf("s.tlscert: %v", err) 172 } 173 if !tlscert.Leaf.NotAfter.After(after) { 174 t.Errorf("state leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after) 175 } 176 } 177 178 // trigger renew 179 hello := clientHelloInfo(exampleDomain, algECDSA) 180 if _, err := man.GetCertificate(hello); err != nil { 181 t.Fatal(err) 182 } 183 184 // wait for renew loop 185 select { 186 case <-time.After(10 * time.Second): 187 t.Fatal("renew took too long to occur") 188 case <-done: 189 } 190} 191 192func TestRenewFromCacheAlreadyRenewed(t *testing.T) { 193 man := &Manager{ 194 Prompt: AcceptTOS, 195 Cache: newMemCache(t), 196 RenewBefore: 24 * time.Hour, 197 Client: &acme.Client{ 198 DirectoryURL: "invalid", 199 }, 200 } 201 defer man.stopRenew() 202 203 // cache a recently renewed cert with a different private key 204 newKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 205 if err != nil { 206 t.Fatal(err) 207 } 208 now := time.Now() 209 newCert, err := dateDummyCert(newKey.Public(), now.Add(-2*time.Hour), now.Add(time.Hour*24*90), exampleDomain) 210 if err != nil { 211 t.Fatal(err) 212 } 213 newLeaf, err := validCert(exampleCertKey, [][]byte{newCert}, newKey, now) 214 if err != nil { 215 t.Fatal(err) 216 } 217 newTLSCert := &tls.Certificate{PrivateKey: newKey, Certificate: [][]byte{newCert}, Leaf: newLeaf} 218 if err := man.cachePut(context.Background(), exampleCertKey, newTLSCert); err != nil { 219 t.Fatal(err) 220 } 221 222 // set internal state to an almost expired cert 223 key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 224 if err != nil { 225 t.Fatal(err) 226 } 227 oldCert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), exampleDomain) 228 if err != nil { 229 t.Fatal(err) 230 } 231 oldLeaf, err := validCert(exampleCertKey, [][]byte{oldCert}, key, now) 232 if err != nil { 233 t.Fatal(err) 234 } 235 man.stateMu.Lock() 236 if man.state == nil { 237 man.state = make(map[certKey]*certState) 238 } 239 s := &certState{ 240 key: key, 241 cert: [][]byte{oldCert}, 242 leaf: oldLeaf, 243 } 244 man.state[exampleCertKey] = s 245 man.stateMu.Unlock() 246 247 // veriy the renewal accepted the newer cached cert 248 defer func() { 249 testDidRenewLoop = func(next time.Duration, err error) {} 250 }() 251 done := make(chan struct{}) 252 testDidRenewLoop = func(next time.Duration, err error) { 253 defer close(done) 254 if err != nil { 255 t.Errorf("testDidRenewLoop: %v", err) 256 } 257 // Next should be about 90 days 258 // Previous expiration was within 1 min. 259 future := 88 * 24 * time.Hour 260 if next < future { 261 t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future) 262 } 263 264 // ensure the cached cert was not modified 265 tlscert, err := man.cacheGet(context.Background(), exampleCertKey) 266 if err != nil { 267 t.Fatalf("man.cacheGet: %v", err) 268 } 269 if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) { 270 t.Errorf("cache leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter) 271 } 272 273 // verify the old cert is also replaced in memory 274 man.stateMu.Lock() 275 defer man.stateMu.Unlock() 276 s := man.state[exampleCertKey] 277 if s == nil { 278 t.Fatalf("m.state[%q] is nil", exampleCertKey) 279 } 280 stateKey := s.key.Public().(*ecdsa.PublicKey) 281 if stateKey.X.Cmp(newKey.X) != 0 || stateKey.Y.Cmp(newKey.Y) != 0 { 282 t.Fatalf("state key was not updated from cache x: %v y: %v; want x: %v y: %v", stateKey.X, stateKey.Y, newKey.X, newKey.Y) 283 } 284 tlscert, err = s.tlscert() 285 if err != nil { 286 t.Fatalf("s.tlscert: %v", err) 287 } 288 if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) { 289 t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter) 290 } 291 292 // verify the private key is replaced in the renewal state 293 r := man.renewal[exampleCertKey] 294 if r == nil { 295 t.Fatalf("m.renewal[%q] is nil", exampleCertKey) 296 } 297 renewalKey := r.key.Public().(*ecdsa.PublicKey) 298 if renewalKey.X.Cmp(newKey.X) != 0 || renewalKey.Y.Cmp(newKey.Y) != 0 { 299 t.Fatalf("renewal private key was not updated from cache x: %v y: %v; want x: %v y: %v", renewalKey.X, renewalKey.Y, newKey.X, newKey.Y) 300 } 301 302 } 303 304 // assert the expiring cert is returned from state 305 hello := clientHelloInfo(exampleDomain, algECDSA) 306 tlscert, err := man.GetCertificate(hello) 307 if err != nil { 308 t.Fatal(err) 309 } 310 if !oldLeaf.NotAfter.Equal(tlscert.Leaf.NotAfter) { 311 t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, oldLeaf.NotAfter) 312 } 313 314 // trigger renew 315 go man.renew(exampleCertKey, s.key, s.leaf.NotAfter) 316 317 // wait for renew loop 318 select { 319 case <-time.After(10 * time.Second): 320 t.Fatal("renew took too long to occur") 321 case <-done: 322 // assert the new cert is returned from state after renew 323 hello := clientHelloInfo(exampleDomain, algECDSA) 324 tlscert, err := man.GetCertificate(hello) 325 if err != nil { 326 t.Fatal(err) 327 } 328 if !newTLSCert.Leaf.NotAfter.Equal(tlscert.Leaf.NotAfter) { 329 t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newTLSCert.Leaf.NotAfter) 330 } 331 } 332} 333