5d1c63fffbb8530b8b274ec79b68db06dac07484
[platform/core/system/edge-orchestration.git] / vendor / github.com / miekg / dns / vendor / golang.org / x / crypto / acme / autocert / renewal_test.go
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/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
24 func 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
50 func 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                 // cert request
75                 case "/new-cert":
76                         var req struct {
77                                 CSR string `json:"csr"`
78                         }
79                         decodePayload(&req, r.Body)
80                         b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
81                         csr, err := x509.ParseCertificateRequest(b)
82                         if err != nil {
83                                 t.Fatalf("new-cert: CSR: %v", err)
84                         }
85                         der, err := dummyCert(csr.PublicKey, exampleDomain)
86                         if err != nil {
87                                 t.Fatalf("new-cert: dummyCert: %v", err)
88                         }
89                         chainUp := fmt.Sprintf("<%s/ca-cert>; rel=up", ca.URL)
90                         w.Header().Set("Link", chainUp)
91                         w.WriteHeader(http.StatusCreated)
92                         w.Write(der)
93                 // CA chain cert
94                 case "/ca-cert":
95                         der, err := dummyCert(nil, "ca")
96                         if err != nil {
97                                 t.Fatalf("ca-cert: dummyCert: %v", err)
98                         }
99                         w.Write(der)
100                 default:
101                         t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path)
102                 }
103         }))
104         defer ca.Close()
105
106         man := &Manager{
107                 Prompt:      AcceptTOS,
108                 Cache:       newMemCache(t),
109                 RenewBefore: 24 * time.Hour,
110                 Client: &acme.Client{
111                         DirectoryURL: ca.URL,
112                 },
113         }
114         defer man.stopRenew()
115
116         // cache an almost expired cert
117         key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
118         if err != nil {
119                 t.Fatal(err)
120         }
121         now := time.Now()
122         cert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), exampleDomain)
123         if err != nil {
124                 t.Fatal(err)
125         }
126         tlscert := &tls.Certificate{PrivateKey: key, Certificate: [][]byte{cert}}
127         if err := man.cachePut(context.Background(), exampleCertKey, tlscert); err != nil {
128                 t.Fatal(err)
129         }
130
131         // veriy the renewal happened
132         defer func() {
133                 testDidRenewLoop = func(next time.Duration, err error) {}
134         }()
135         done := make(chan struct{})
136         testDidRenewLoop = func(next time.Duration, err error) {
137                 defer close(done)
138                 if err != nil {
139                         t.Errorf("testDidRenewLoop: %v", err)
140                 }
141                 // Next should be about 90 days:
142                 // dummyCert creates 90days expiry + account for man.RenewBefore.
143                 // Previous expiration was within 1 min.
144                 future := 88 * 24 * time.Hour
145                 if next < future {
146                         t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
147                 }
148
149                 // ensure the new cert is cached
150                 after := time.Now().Add(future)
151                 tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
152                 if err != nil {
153                         t.Fatalf("man.cacheGet: %v", err)
154                 }
155                 if !tlscert.Leaf.NotAfter.After(after) {
156                         t.Errorf("cache leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
157                 }
158
159                 // verify the old cert is also replaced in memory
160                 man.stateMu.Lock()
161                 defer man.stateMu.Unlock()
162                 s := man.state[exampleCertKey]
163                 if s == nil {
164                         t.Fatalf("m.state[%q] is nil", exampleCertKey)
165                 }
166                 tlscert, err = s.tlscert()
167                 if err != nil {
168                         t.Fatalf("s.tlscert: %v", err)
169                 }
170                 if !tlscert.Leaf.NotAfter.After(after) {
171                         t.Errorf("state leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
172                 }
173         }
174
175         // trigger renew
176         hello := clientHelloInfo(exampleDomain, true)
177         if _, err := man.GetCertificate(hello); err != nil {
178                 t.Fatal(err)
179         }
180
181         // wait for renew loop
182         select {
183         case <-time.After(10 * time.Second):
184                 t.Fatal("renew took too long to occur")
185         case <-done:
186         }
187 }
188
189 func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
190         man := &Manager{
191                 Prompt:      AcceptTOS,
192                 Cache:       newMemCache(t),
193                 RenewBefore: 24 * time.Hour,
194                 Client: &acme.Client{
195                         DirectoryURL: "invalid",
196                 },
197         }
198         defer man.stopRenew()
199
200         // cache a recently renewed cert with a different private key
201         newKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
202         if err != nil {
203                 t.Fatal(err)
204         }
205         now := time.Now()
206         newCert, err := dateDummyCert(newKey.Public(), now.Add(-2*time.Hour), now.Add(time.Hour*24*90), exampleDomain)
207         if err != nil {
208                 t.Fatal(err)
209         }
210         newLeaf, err := validCert(exampleCertKey, [][]byte{newCert}, newKey, now)
211         if err != nil {
212                 t.Fatal(err)
213         }
214         newTLSCert := &tls.Certificate{PrivateKey: newKey, Certificate: [][]byte{newCert}, Leaf: newLeaf}
215         if err := man.cachePut(context.Background(), exampleCertKey, newTLSCert); err != nil {
216                 t.Fatal(err)
217         }
218
219         // set internal state to an almost expired cert
220         key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
221         if err != nil {
222                 t.Fatal(err)
223         }
224         oldCert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), exampleDomain)
225         if err != nil {
226                 t.Fatal(err)
227         }
228         oldLeaf, err := validCert(exampleCertKey, [][]byte{oldCert}, key, now)
229         if err != nil {
230                 t.Fatal(err)
231         }
232         man.stateMu.Lock()
233         if man.state == nil {
234                 man.state = make(map[certKey]*certState)
235         }
236         s := &certState{
237                 key:  key,
238                 cert: [][]byte{oldCert},
239                 leaf: oldLeaf,
240         }
241         man.state[exampleCertKey] = s
242         man.stateMu.Unlock()
243
244         // veriy the renewal accepted the newer cached cert
245         defer func() {
246                 testDidRenewLoop = func(next time.Duration, err error) {}
247         }()
248         done := make(chan struct{})
249         testDidRenewLoop = func(next time.Duration, err error) {
250                 defer close(done)
251                 if err != nil {
252                         t.Errorf("testDidRenewLoop: %v", err)
253                 }
254                 // Next should be about 90 days
255                 // Previous expiration was within 1 min.
256                 future := 88 * 24 * time.Hour
257                 if next < future {
258                         t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
259                 }
260
261                 // ensure the cached cert was not modified
262                 tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
263                 if err != nil {
264                         t.Fatalf("man.cacheGet: %v", err)
265                 }
266                 if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
267                         t.Errorf("cache leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
268                 }
269
270                 // verify the old cert is also replaced in memory
271                 man.stateMu.Lock()
272                 defer man.stateMu.Unlock()
273                 s := man.state[exampleCertKey]
274                 if s == nil {
275                         t.Fatalf("m.state[%q] is nil", exampleCertKey)
276                 }
277                 stateKey := s.key.Public().(*ecdsa.PublicKey)
278                 if stateKey.X.Cmp(newKey.X) != 0 || stateKey.Y.Cmp(newKey.Y) != 0 {
279                         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)
280                 }
281                 tlscert, err = s.tlscert()
282                 if err != nil {
283                         t.Fatalf("s.tlscert: %v", err)
284                 }
285                 if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
286                         t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
287                 }
288
289                 // verify the private key is replaced in the renewal state
290                 r := man.renewal[exampleCertKey]
291                 if r == nil {
292                         t.Fatalf("m.renewal[%q] is nil", exampleCertKey)
293                 }
294                 renewalKey := r.key.Public().(*ecdsa.PublicKey)
295                 if renewalKey.X.Cmp(newKey.X) != 0 || renewalKey.Y.Cmp(newKey.Y) != 0 {
296                         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)
297                 }
298
299         }
300
301         // assert the expiring cert is returned from state
302         hello := clientHelloInfo(exampleDomain, true)
303         tlscert, err := man.GetCertificate(hello)
304         if err != nil {
305                 t.Fatal(err)
306         }
307         if !oldLeaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
308                 t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, oldLeaf.NotAfter)
309         }
310
311         // trigger renew
312         go man.renew(exampleCertKey, s.key, s.leaf.NotAfter)
313
314         // wait for renew loop
315         select {
316         case <-time.After(10 * time.Second):
317                 t.Fatal("renew took too long to occur")
318         case <-done:
319                 // assert the new cert is returned from state after renew
320                 hello := clientHelloInfo(exampleDomain, true)
321                 tlscert, err := man.GetCertificate(hello)
322                 if err != nil {
323                         t.Fatal(err)
324                 }
325                 if !newTLSCert.Leaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
326                         t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newTLSCert.Leaf.NotAfter)
327                 }
328         }
329 }