1
2
3
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
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
63 defer func() {
64
65
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
76
77
78 }
79 }()
80
81 if err != nil {
82 t.Errorf("testDidRenewLoop: %v", err)
83 }
84
85
86
87 future := 88 * 24 * time.Hour
88 if next < future {
89 t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
90 }
91
92
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
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
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
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
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
170 defer func() {
171
172
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
183
184
185 }
186 }()
187
188 if err != nil {
189 t.Errorf("testDidRenewLoop: %v", err)
190 }
191
192
193 future := 88 * 24 * time.Hour
194 if next < future {
195 t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
196 }
197
198
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
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
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
242 man.startRenew(exampleCertKey, s.key, s.leaf.NotAfter)
243 <-renewed
244 func() {
245 man.renewalMu.Lock()
246 defer man.renewalMu.Unlock()
247
248
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
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