~juju-qa/ubuntu/xenial/juju/2.0-rc2

« back to all changes in this revision

Viewing changes to src/golang.org/x/crypto/acme/acme_test.go

  • Committer: Nicholas Skaggs
  • Date: 2016-09-30 14:39:30 UTC
  • mfrom: (1.8.1)
  • Revision ID: nicholas.skaggs@canonical.com-20160930143930-vwwhrefh6ftckccy
import upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2015 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 acme
 
6
 
 
7
import (
 
8
        "bytes"
 
9
        "crypto/rand"
 
10
        "crypto/rsa"
 
11
        "crypto/tls"
 
12
        "crypto/x509"
 
13
        "crypto/x509/pkix"
 
14
        "encoding/base64"
 
15
        "encoding/json"
 
16
        "fmt"
 
17
        "io/ioutil"
 
18
        "math/big"
 
19
        "net/http"
 
20
        "net/http/httptest"
 
21
        "reflect"
 
22
        "sort"
 
23
        "strings"
 
24
        "testing"
 
25
        "time"
 
26
 
 
27
        "golang.org/x/net/context"
 
28
)
 
29
 
 
30
// Decodes a JWS-encoded request and unmarshals the decoded JSON into a provided
 
31
// interface.
 
32
func decodeJWSRequest(t *testing.T, v interface{}, r *http.Request) {
 
33
        // Decode request
 
34
        var req struct{ Payload string }
 
35
        if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
 
36
                t.Fatal(err)
 
37
        }
 
38
        payload, err := base64.RawURLEncoding.DecodeString(req.Payload)
 
39
        if err != nil {
 
40
                t.Fatal(err)
 
41
        }
 
42
        err = json.Unmarshal(payload, v)
 
43
        if err != nil {
 
44
                t.Fatal(err)
 
45
        }
 
46
}
 
47
 
 
48
func TestDiscover(t *testing.T) {
 
49
        const (
 
50
                reg    = "https://example.com/acme/new-reg"
 
51
                authz  = "https://example.com/acme/new-authz"
 
52
                cert   = "https://example.com/acme/new-cert"
 
53
                revoke = "https://example.com/acme/revoke-cert"
 
54
        )
 
55
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
56
                w.Header().Set("content-type", "application/json")
 
57
                fmt.Fprintf(w, `{
 
58
                        "new-reg": %q,
 
59
                        "new-authz": %q,
 
60
                        "new-cert": %q,
 
61
                        "revoke-cert": %q
 
62
                }`, reg, authz, cert, revoke)
 
63
        }))
 
64
        defer ts.Close()
 
65
        c := Client{DirectoryURL: ts.URL}
 
66
        dir, err := c.Discover(context.Background())
 
67
        if err != nil {
 
68
                t.Fatal(err)
 
69
        }
 
70
        if dir.RegURL != reg {
 
71
                t.Errorf("dir.RegURL = %q; want %q", dir.RegURL, reg)
 
72
        }
 
73
        if dir.AuthzURL != authz {
 
74
                t.Errorf("dir.AuthzURL = %q; want %q", dir.AuthzURL, authz)
 
75
        }
 
76
        if dir.CertURL != cert {
 
77
                t.Errorf("dir.CertURL = %q; want %q", dir.CertURL, cert)
 
78
        }
 
79
        if dir.RevokeURL != revoke {
 
80
                t.Errorf("dir.RevokeURL = %q; want %q", dir.RevokeURL, revoke)
 
81
        }
 
82
}
 
83
 
 
84
func TestRegister(t *testing.T) {
 
85
        contacts := []string{"mailto:admin@example.com"}
 
86
 
 
87
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
88
                if r.Method == "HEAD" {
 
89
                        w.Header().Set("replay-nonce", "test-nonce")
 
90
                        return
 
91
                }
 
92
                if r.Method != "POST" {
 
93
                        t.Errorf("r.Method = %q; want POST", r.Method)
 
94
                }
 
95
 
 
96
                var j struct {
 
97
                        Resource  string
 
98
                        Contact   []string
 
99
                        Agreement string
 
100
                }
 
101
                decodeJWSRequest(t, &j, r)
 
102
 
 
103
                // Test request
 
104
                if j.Resource != "new-reg" {
 
105
                        t.Errorf("j.Resource = %q; want new-reg", j.Resource)
 
106
                }
 
107
                if !reflect.DeepEqual(j.Contact, contacts) {
 
108
                        t.Errorf("j.Contact = %v; want %v", j.Contact, contacts)
 
109
                }
 
110
 
 
111
                w.Header().Set("Location", "https://ca.tld/acme/reg/1")
 
112
                w.Header().Set("Link", `<https://ca.tld/acme/new-authz>;rel="next"`)
 
113
                w.Header().Add("Link", `<https://ca.tld/acme/recover-reg>;rel="recover"`)
 
114
                w.Header().Add("Link", `<https://ca.tld/acme/terms>;rel="terms-of-service"`)
 
115
                w.WriteHeader(http.StatusCreated)
 
116
                b, _ := json.Marshal(contacts)
 
117
                fmt.Fprintf(w, `{"contact": %s}`, b)
 
118
        }))
 
119
        defer ts.Close()
 
120
 
 
121
        prompt := func(url string) bool {
 
122
                const terms = "https://ca.tld/acme/terms"
 
123
                if url != terms {
 
124
                        t.Errorf("prompt url = %q; want %q", url, terms)
 
125
                }
 
126
                return false
 
127
        }
 
128
 
 
129
        c := Client{Key: testKeyEC, dir: &Directory{RegURL: ts.URL}}
 
130
        a := &Account{Contact: contacts}
 
131
        var err error
 
132
        if a, err = c.Register(context.Background(), a, prompt); err != nil {
 
133
                t.Fatal(err)
 
134
        }
 
135
        if a.URI != "https://ca.tld/acme/reg/1" {
 
136
                t.Errorf("a.URI = %q; want https://ca.tld/acme/reg/1", a.URI)
 
137
        }
 
138
        if a.Authz != "https://ca.tld/acme/new-authz" {
 
139
                t.Errorf("a.Authz = %q; want https://ca.tld/acme/new-authz", a.Authz)
 
140
        }
 
141
        if a.CurrentTerms != "https://ca.tld/acme/terms" {
 
142
                t.Errorf("a.CurrentTerms = %q; want https://ca.tld/acme/terms", a.CurrentTerms)
 
143
        }
 
144
        if !reflect.DeepEqual(a.Contact, contacts) {
 
145
                t.Errorf("a.Contact = %v; want %v", a.Contact, contacts)
 
146
        }
 
147
}
 
148
 
 
149
func TestUpdateReg(t *testing.T) {
 
150
        const terms = "https://ca.tld/acme/terms"
 
151
        contacts := []string{"mailto:admin@example.com"}
 
152
 
 
153
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
154
                if r.Method == "HEAD" {
 
155
                        w.Header().Set("replay-nonce", "test-nonce")
 
156
                        return
 
157
                }
 
158
                if r.Method != "POST" {
 
159
                        t.Errorf("r.Method = %q; want POST", r.Method)
 
160
                }
 
161
 
 
162
                var j struct {
 
163
                        Resource  string
 
164
                        Contact   []string
 
165
                        Agreement string
 
166
                }
 
167
                decodeJWSRequest(t, &j, r)
 
168
 
 
169
                // Test request
 
170
                if j.Resource != "reg" {
 
171
                        t.Errorf("j.Resource = %q; want reg", j.Resource)
 
172
                }
 
173
                if j.Agreement != terms {
 
174
                        t.Errorf("j.Agreement = %q; want %q", j.Agreement, terms)
 
175
                }
 
176
                if !reflect.DeepEqual(j.Contact, contacts) {
 
177
                        t.Errorf("j.Contact = %v; want %v", j.Contact, contacts)
 
178
                }
 
179
 
 
180
                w.Header().Set("Link", `<https://ca.tld/acme/new-authz>;rel="next"`)
 
181
                w.Header().Add("Link", `<https://ca.tld/acme/recover-reg>;rel="recover"`)
 
182
                w.Header().Add("Link", fmt.Sprintf(`<%s>;rel="terms-of-service"`, terms))
 
183
                w.WriteHeader(http.StatusOK)
 
184
                b, _ := json.Marshal(contacts)
 
185
                fmt.Fprintf(w, `{"contact":%s, "agreement":%q}`, b, terms)
 
186
        }))
 
187
        defer ts.Close()
 
188
 
 
189
        c := Client{Key: testKeyEC}
 
190
        a := &Account{URI: ts.URL, Contact: contacts, AgreedTerms: terms}
 
191
        var err error
 
192
        if a, err = c.UpdateReg(context.Background(), a); err != nil {
 
193
                t.Fatal(err)
 
194
        }
 
195
        if a.Authz != "https://ca.tld/acme/new-authz" {
 
196
                t.Errorf("a.Authz = %q; want https://ca.tld/acme/new-authz", a.Authz)
 
197
        }
 
198
        if a.AgreedTerms != terms {
 
199
                t.Errorf("a.AgreedTerms = %q; want %q", a.AgreedTerms, terms)
 
200
        }
 
201
        if a.CurrentTerms != terms {
 
202
                t.Errorf("a.CurrentTerms = %q; want %q", a.CurrentTerms, terms)
 
203
        }
 
204
        if a.URI != ts.URL {
 
205
                t.Errorf("a.URI = %q; want %q", a.URI, ts.URL)
 
206
        }
 
207
}
 
208
 
 
209
func TestGetReg(t *testing.T) {
 
210
        const terms = "https://ca.tld/acme/terms"
 
211
        const newTerms = "https://ca.tld/acme/new-terms"
 
212
        contacts := []string{"mailto:admin@example.com"}
 
213
 
 
214
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
215
                if r.Method == "HEAD" {
 
216
                        w.Header().Set("replay-nonce", "test-nonce")
 
217
                        return
 
218
                }
 
219
                if r.Method != "POST" {
 
220
                        t.Errorf("r.Method = %q; want POST", r.Method)
 
221
                }
 
222
 
 
223
                var j struct {
 
224
                        Resource  string
 
225
                        Contact   []string
 
226
                        Agreement string
 
227
                }
 
228
                decodeJWSRequest(t, &j, r)
 
229
 
 
230
                // Test request
 
231
                if j.Resource != "reg" {
 
232
                        t.Errorf("j.Resource = %q; want reg", j.Resource)
 
233
                }
 
234
                if len(j.Contact) != 0 {
 
235
                        t.Errorf("j.Contact = %v", j.Contact)
 
236
                }
 
237
                if j.Agreement != "" {
 
238
                        t.Errorf("j.Agreement = %q", j.Agreement)
 
239
                }
 
240
 
 
241
                w.Header().Set("Link", `<https://ca.tld/acme/new-authz>;rel="next"`)
 
242
                w.Header().Add("Link", `<https://ca.tld/acme/recover-reg>;rel="recover"`)
 
243
                w.Header().Add("Link", fmt.Sprintf(`<%s>;rel="terms-of-service"`, newTerms))
 
244
                w.WriteHeader(http.StatusOK)
 
245
                b, _ := json.Marshal(contacts)
 
246
                fmt.Fprintf(w, `{"contact":%s, "agreement":%q}`, b, terms)
 
247
        }))
 
248
        defer ts.Close()
 
249
 
 
250
        c := Client{Key: testKeyEC}
 
251
        a, err := c.GetReg(context.Background(), ts.URL)
 
252
        if err != nil {
 
253
                t.Fatal(err)
 
254
        }
 
255
        if a.Authz != "https://ca.tld/acme/new-authz" {
 
256
                t.Errorf("a.AuthzURL = %q; want https://ca.tld/acme/new-authz", a.Authz)
 
257
        }
 
258
        if a.AgreedTerms != terms {
 
259
                t.Errorf("a.AgreedTerms = %q; want %q", a.AgreedTerms, terms)
 
260
        }
 
261
        if a.CurrentTerms != newTerms {
 
262
                t.Errorf("a.CurrentTerms = %q; want %q", a.CurrentTerms, newTerms)
 
263
        }
 
264
        if a.URI != ts.URL {
 
265
                t.Errorf("a.URI = %q; want %q", a.URI, ts.URL)
 
266
        }
 
267
}
 
268
 
 
269
func TestAuthorize(t *testing.T) {
 
270
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
271
                if r.Method == "HEAD" {
 
272
                        w.Header().Set("replay-nonce", "test-nonce")
 
273
                        return
 
274
                }
 
275
                if r.Method != "POST" {
 
276
                        t.Errorf("r.Method = %q; want POST", r.Method)
 
277
                }
 
278
 
 
279
                var j struct {
 
280
                        Resource   string
 
281
                        Identifier struct {
 
282
                                Type  string
 
283
                                Value string
 
284
                        }
 
285
                }
 
286
                decodeJWSRequest(t, &j, r)
 
287
 
 
288
                // Test request
 
289
                if j.Resource != "new-authz" {
 
290
                        t.Errorf("j.Resource = %q; want new-authz", j.Resource)
 
291
                }
 
292
                if j.Identifier.Type != "dns" {
 
293
                        t.Errorf("j.Identifier.Type = %q; want dns", j.Identifier.Type)
 
294
                }
 
295
                if j.Identifier.Value != "example.com" {
 
296
                        t.Errorf("j.Identifier.Value = %q; want example.com", j.Identifier.Value)
 
297
                }
 
298
 
 
299
                w.Header().Set("Location", "https://ca.tld/acme/auth/1")
 
300
                w.WriteHeader(http.StatusCreated)
 
301
                fmt.Fprintf(w, `{
 
302
                        "identifier": {"type":"dns","value":"example.com"},
 
303
                        "status":"pending",
 
304
                        "challenges":[
 
305
                                {
 
306
                                        "type":"http-01",
 
307
                                        "status":"pending",
 
308
                                        "uri":"https://ca.tld/acme/challenge/publickey/id1",
 
309
                                        "token":"token1"
 
310
                                },
 
311
                                {
 
312
                                        "type":"tls-sni-01",
 
313
                                        "status":"pending",
 
314
                                        "uri":"https://ca.tld/acme/challenge/publickey/id2",
 
315
                                        "token":"token2"
 
316
                                }
 
317
                        ],
 
318
                        "combinations":[[0],[1]]}`)
 
319
        }))
 
320
        defer ts.Close()
 
321
 
 
322
        cl := Client{Key: testKeyEC, dir: &Directory{AuthzURL: ts.URL}}
 
323
        auth, err := cl.Authorize(context.Background(), "example.com")
 
324
        if err != nil {
 
325
                t.Fatal(err)
 
326
        }
 
327
 
 
328
        if auth.URI != "https://ca.tld/acme/auth/1" {
 
329
                t.Errorf("URI = %q; want https://ca.tld/acme/auth/1", auth.URI)
 
330
        }
 
331
        if auth.Status != "pending" {
 
332
                t.Errorf("Status = %q; want pending", auth.Status)
 
333
        }
 
334
        if auth.Identifier.Type != "dns" {
 
335
                t.Errorf("Identifier.Type = %q; want dns", auth.Identifier.Type)
 
336
        }
 
337
        if auth.Identifier.Value != "example.com" {
 
338
                t.Errorf("Identifier.Value = %q; want example.com", auth.Identifier.Value)
 
339
        }
 
340
 
 
341
        if n := len(auth.Challenges); n != 2 {
 
342
                t.Fatalf("len(auth.Challenges) = %d; want 2", n)
 
343
        }
 
344
 
 
345
        c := auth.Challenges[0]
 
346
        if c.Type != "http-01" {
 
347
                t.Errorf("c.Type = %q; want http-01", c.Type)
 
348
        }
 
349
        if c.URI != "https://ca.tld/acme/challenge/publickey/id1" {
 
350
                t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", c.URI)
 
351
        }
 
352
        if c.Token != "token1" {
 
353
                t.Errorf("c.Token = %q; want token1", c.Type)
 
354
        }
 
355
 
 
356
        c = auth.Challenges[1]
 
357
        if c.Type != "tls-sni-01" {
 
358
                t.Errorf("c.Type = %q; want tls-sni-01", c.Type)
 
359
        }
 
360
        if c.URI != "https://ca.tld/acme/challenge/publickey/id2" {
 
361
                t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id2", c.URI)
 
362
        }
 
363
        if c.Token != "token2" {
 
364
                t.Errorf("c.Token = %q; want token2", c.Type)
 
365
        }
 
366
 
 
367
        combs := [][]int{{0}, {1}}
 
368
        if !reflect.DeepEqual(auth.Combinations, combs) {
 
369
                t.Errorf("auth.Combinations: %+v\nwant: %+v\n", auth.Combinations, combs)
 
370
        }
 
371
}
 
372
 
 
373
func TestAuthorizeValid(t *testing.T) {
 
374
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
375
                if r.Method == "HEAD" {
 
376
                        w.Header().Set("replay-nonce", "nonce")
 
377
                        return
 
378
                }
 
379
                w.WriteHeader(http.StatusCreated)
 
380
                w.Write([]byte(`{"status":"valid"}`))
 
381
        }))
 
382
        defer ts.Close()
 
383
        client := Client{Key: testKey, dir: &Directory{AuthzURL: ts.URL}}
 
384
        _, err := client.Authorize(context.Background(), "example.com")
 
385
        if err != nil {
 
386
                t.Errorf("err = %v", err)
 
387
        }
 
388
}
 
389
 
 
390
func TestGetAuthorization(t *testing.T) {
 
391
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
392
                if r.Method != "GET" {
 
393
                        t.Errorf("r.Method = %q; want GET", r.Method)
 
394
                }
 
395
 
 
396
                w.WriteHeader(http.StatusOK)
 
397
                fmt.Fprintf(w, `{
 
398
                        "identifier": {"type":"dns","value":"example.com"},
 
399
                        "status":"pending",
 
400
                        "challenges":[
 
401
                                {
 
402
                                        "type":"http-01",
 
403
                                        "status":"pending",
 
404
                                        "uri":"https://ca.tld/acme/challenge/publickey/id1",
 
405
                                        "token":"token1"
 
406
                                },
 
407
                                {
 
408
                                        "type":"tls-sni-01",
 
409
                                        "status":"pending",
 
410
                                        "uri":"https://ca.tld/acme/challenge/publickey/id2",
 
411
                                        "token":"token2"
 
412
                                }
 
413
                        ],
 
414
                        "combinations":[[0],[1]]}`)
 
415
        }))
 
416
        defer ts.Close()
 
417
 
 
418
        cl := Client{Key: testKeyEC}
 
419
        auth, err := cl.GetAuthorization(context.Background(), ts.URL)
 
420
        if err != nil {
 
421
                t.Fatal(err)
 
422
        }
 
423
 
 
424
        if auth.Status != "pending" {
 
425
                t.Errorf("Status = %q; want pending", auth.Status)
 
426
        }
 
427
        if auth.Identifier.Type != "dns" {
 
428
                t.Errorf("Identifier.Type = %q; want dns", auth.Identifier.Type)
 
429
        }
 
430
        if auth.Identifier.Value != "example.com" {
 
431
                t.Errorf("Identifier.Value = %q; want example.com", auth.Identifier.Value)
 
432
        }
 
433
 
 
434
        if n := len(auth.Challenges); n != 2 {
 
435
                t.Fatalf("len(set.Challenges) = %d; want 2", n)
 
436
        }
 
437
 
 
438
        c := auth.Challenges[0]
 
439
        if c.Type != "http-01" {
 
440
                t.Errorf("c.Type = %q; want http-01", c.Type)
 
441
        }
 
442
        if c.URI != "https://ca.tld/acme/challenge/publickey/id1" {
 
443
                t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", c.URI)
 
444
        }
 
445
        if c.Token != "token1" {
 
446
                t.Errorf("c.Token = %q; want token1", c.Type)
 
447
        }
 
448
 
 
449
        c = auth.Challenges[1]
 
450
        if c.Type != "tls-sni-01" {
 
451
                t.Errorf("c.Type = %q; want tls-sni-01", c.Type)
 
452
        }
 
453
        if c.URI != "https://ca.tld/acme/challenge/publickey/id2" {
 
454
                t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id2", c.URI)
 
455
        }
 
456
        if c.Token != "token2" {
 
457
                t.Errorf("c.Token = %q; want token2", c.Type)
 
458
        }
 
459
 
 
460
        combs := [][]int{{0}, {1}}
 
461
        if !reflect.DeepEqual(auth.Combinations, combs) {
 
462
                t.Errorf("auth.Combinations: %+v\nwant: %+v\n", auth.Combinations, combs)
 
463
        }
 
464
}
 
465
 
 
466
func TestWaitAuthorization(t *testing.T) {
 
467
        var count int
 
468
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
469
                count++
 
470
                w.Header().Set("retry-after", "0")
 
471
                if count > 1 {
 
472
                        fmt.Fprintf(w, `{"status":"valid"}`)
 
473
                        return
 
474
                }
 
475
                fmt.Fprintf(w, `{"status":"pending"}`)
 
476
        }))
 
477
        defer ts.Close()
 
478
 
 
479
        type res struct {
 
480
                authz *Authorization
 
481
                err   error
 
482
        }
 
483
        done := make(chan res)
 
484
        defer close(done)
 
485
        go func() {
 
486
                var client Client
 
487
                a, err := client.WaitAuthorization(context.Background(), ts.URL)
 
488
                done <- res{a, err}
 
489
        }()
 
490
 
 
491
        select {
 
492
        case <-time.After(5 * time.Second):
 
493
                t.Fatal("WaitAuthz took too long to return")
 
494
        case res := <-done:
 
495
                if res.err != nil {
 
496
                        t.Fatalf("res.err =  %v", res.err)
 
497
                }
 
498
                if res.authz == nil {
 
499
                        t.Fatal("res.authz is nil")
 
500
                }
 
501
        }
 
502
}
 
503
 
 
504
func TestWaitAuthorizationInvalid(t *testing.T) {
 
505
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
506
                fmt.Fprintf(w, `{"status":"invalid"}`)
 
507
        }))
 
508
        defer ts.Close()
 
509
 
 
510
        res := make(chan error)
 
511
        defer close(res)
 
512
        go func() {
 
513
                var client Client
 
514
                _, err := client.WaitAuthorization(context.Background(), ts.URL)
 
515
                res <- err
 
516
        }()
 
517
 
 
518
        select {
 
519
        case <-time.After(3 * time.Second):
 
520
                t.Fatal("WaitAuthz took too long to return")
 
521
        case err := <-res:
 
522
                if err == nil {
 
523
                        t.Error("err is nil")
 
524
                }
 
525
        }
 
526
}
 
527
 
 
528
func TestWaitAuthorizationCancel(t *testing.T) {
 
529
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
530
                w.Header().Set("retry-after", "60")
 
531
                fmt.Fprintf(w, `{"status":"pending"}`)
 
532
        }))
 
533
        defer ts.Close()
 
534
 
 
535
        res := make(chan error)
 
536
        defer close(res)
 
537
        go func() {
 
538
                var client Client
 
539
                ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
 
540
                defer cancel()
 
541
                _, err := client.WaitAuthorization(ctx, ts.URL)
 
542
                res <- err
 
543
        }()
 
544
 
 
545
        select {
 
546
        case <-time.After(time.Second):
 
547
                t.Fatal("WaitAuthz took too long to return")
 
548
        case err := <-res:
 
549
                if err == nil {
 
550
                        t.Error("err is nil")
 
551
                }
 
552
        }
 
553
}
 
554
 
 
555
func TestRevokeAuthorization(t *testing.T) {
 
556
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
557
                if r.Method == "HEAD" {
 
558
                        w.Header().Set("replay-nonce", "nonce")
 
559
                        return
 
560
                }
 
561
                switch r.URL.Path {
 
562
                case "/1":
 
563
                        var req struct {
 
564
                                Resource string
 
565
                                Delete   bool
 
566
                        }
 
567
                        decodeJWSRequest(t, &req, r)
 
568
                        if req.Resource != "authz" {
 
569
                                t.Errorf("req.Resource = %q; want authz", req.Resource)
 
570
                        }
 
571
                        if !req.Delete {
 
572
                                t.Errorf("req.Delete is false")
 
573
                        }
 
574
                case "/2":
 
575
                        w.WriteHeader(http.StatusInternalServerError)
 
576
                }
 
577
        }))
 
578
        defer ts.Close()
 
579
        client := &Client{Key: testKey}
 
580
        ctx := context.Background()
 
581
        if err := client.RevokeAuthorization(ctx, ts.URL+"/1"); err != nil {
 
582
                t.Errorf("err = %v", err)
 
583
        }
 
584
        if client.RevokeAuthorization(ctx, ts.URL+"/2") == nil {
 
585
                t.Error("nil error")
 
586
        }
 
587
}
 
588
 
 
589
func TestPollChallenge(t *testing.T) {
 
590
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
591
                if r.Method != "GET" {
 
592
                        t.Errorf("r.Method = %q; want GET", r.Method)
 
593
                }
 
594
 
 
595
                w.WriteHeader(http.StatusOK)
 
596
                fmt.Fprintf(w, `{
 
597
                        "type":"http-01",
 
598
                        "status":"pending",
 
599
                        "uri":"https://ca.tld/acme/challenge/publickey/id1",
 
600
                        "token":"token1"}`)
 
601
        }))
 
602
        defer ts.Close()
 
603
 
 
604
        cl := Client{Key: testKeyEC}
 
605
        chall, err := cl.GetChallenge(context.Background(), ts.URL)
 
606
        if err != nil {
 
607
                t.Fatal(err)
 
608
        }
 
609
 
 
610
        if chall.Status != "pending" {
 
611
                t.Errorf("Status = %q; want pending", chall.Status)
 
612
        }
 
613
        if chall.Type != "http-01" {
 
614
                t.Errorf("c.Type = %q; want http-01", chall.Type)
 
615
        }
 
616
        if chall.URI != "https://ca.tld/acme/challenge/publickey/id1" {
 
617
                t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", chall.URI)
 
618
        }
 
619
        if chall.Token != "token1" {
 
620
                t.Errorf("c.Token = %q; want token1", chall.Type)
 
621
        }
 
622
}
 
623
 
 
624
func TestAcceptChallenge(t *testing.T) {
 
625
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
626
                if r.Method == "HEAD" {
 
627
                        w.Header().Set("replay-nonce", "test-nonce")
 
628
                        return
 
629
                }
 
630
                if r.Method != "POST" {
 
631
                        t.Errorf("r.Method = %q; want POST", r.Method)
 
632
                }
 
633
 
 
634
                var j struct {
 
635
                        Resource string
 
636
                        Type     string
 
637
                        Auth     string `json:"keyAuthorization"`
 
638
                }
 
639
                decodeJWSRequest(t, &j, r)
 
640
 
 
641
                // Test request
 
642
                if j.Resource != "challenge" {
 
643
                        t.Errorf(`resource = %q; want "challenge"`, j.Resource)
 
644
                }
 
645
                if j.Type != "http-01" {
 
646
                        t.Errorf(`type = %q; want "http-01"`, j.Type)
 
647
                }
 
648
                keyAuth := "token1." + testKeyECThumbprint
 
649
                if j.Auth != keyAuth {
 
650
                        t.Errorf(`keyAuthorization = %q; want %q`, j.Auth, keyAuth)
 
651
                }
 
652
 
 
653
                // Respond to request
 
654
                w.WriteHeader(http.StatusAccepted)
 
655
                fmt.Fprintf(w, `{
 
656
                        "type":"http-01",
 
657
                        "status":"pending",
 
658
                        "uri":"https://ca.tld/acme/challenge/publickey/id1",
 
659
                        "token":"token1",
 
660
                        "keyAuthorization":%q
 
661
                }`, keyAuth)
 
662
        }))
 
663
        defer ts.Close()
 
664
 
 
665
        cl := Client{Key: testKeyEC}
 
666
        c, err := cl.Accept(context.Background(), &Challenge{
 
667
                URI:   ts.URL,
 
668
                Token: "token1",
 
669
                Type:  "http-01",
 
670
        })
 
671
        if err != nil {
 
672
                t.Fatal(err)
 
673
        }
 
674
 
 
675
        if c.Type != "http-01" {
 
676
                t.Errorf("c.Type = %q; want http-01", c.Type)
 
677
        }
 
678
        if c.URI != "https://ca.tld/acme/challenge/publickey/id1" {
 
679
                t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", c.URI)
 
680
        }
 
681
        if c.Token != "token1" {
 
682
                t.Errorf("c.Token = %q; want token1", c.Type)
 
683
        }
 
684
}
 
685
 
 
686
func TestNewCert(t *testing.T) {
 
687
        notBefore := time.Now()
 
688
        notAfter := notBefore.AddDate(0, 2, 0)
 
689
        timeNow = func() time.Time { return notBefore }
 
690
 
 
691
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
692
                if r.Method == "HEAD" {
 
693
                        w.Header().Set("replay-nonce", "test-nonce")
 
694
                        return
 
695
                }
 
696
                if r.Method != "POST" {
 
697
                        t.Errorf("r.Method = %q; want POST", r.Method)
 
698
                }
 
699
 
 
700
                var j struct {
 
701
                        Resource  string `json:"resource"`
 
702
                        CSR       string `json:"csr"`
 
703
                        NotBefore string `json:"notBefore,omitempty"`
 
704
                        NotAfter  string `json:"notAfter,omitempty"`
 
705
                }
 
706
                decodeJWSRequest(t, &j, r)
 
707
 
 
708
                // Test request
 
709
                if j.Resource != "new-cert" {
 
710
                        t.Errorf(`resource = %q; want "new-cert"`, j.Resource)
 
711
                }
 
712
                if j.NotBefore != notBefore.Format(time.RFC3339) {
 
713
                        t.Errorf(`notBefore = %q; wanted %q`, j.NotBefore, notBefore.Format(time.RFC3339))
 
714
                }
 
715
                if j.NotAfter != notAfter.Format(time.RFC3339) {
 
716
                        t.Errorf(`notAfter = %q; wanted %q`, j.NotAfter, notAfter.Format(time.RFC3339))
 
717
                }
 
718
 
 
719
                // Respond to request
 
720
                template := x509.Certificate{
 
721
                        SerialNumber: big.NewInt(int64(1)),
 
722
                        Subject: pkix.Name{
 
723
                                Organization: []string{"goacme"},
 
724
                        },
 
725
                        NotBefore: notBefore,
 
726
                        NotAfter:  notAfter,
 
727
 
 
728
                        KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
 
729
                        ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
 
730
                        BasicConstraintsValid: true,
 
731
                }
 
732
 
 
733
                sampleCert, err := x509.CreateCertificate(rand.Reader, &template, &template, &testKeyEC.PublicKey, testKeyEC)
 
734
                if err != nil {
 
735
                        t.Fatalf("Error creating certificate: %v", err)
 
736
                }
 
737
 
 
738
                w.Header().Set("Location", "https://ca.tld/acme/cert/1")
 
739
                w.WriteHeader(http.StatusCreated)
 
740
                w.Write(sampleCert)
 
741
        }))
 
742
        defer ts.Close()
 
743
 
 
744
        csr := x509.CertificateRequest{
 
745
                Version: 0,
 
746
                Subject: pkix.Name{
 
747
                        CommonName:   "example.com",
 
748
                        Organization: []string{"goacme"},
 
749
                },
 
750
        }
 
751
        csrb, err := x509.CreateCertificateRequest(rand.Reader, &csr, testKeyEC)
 
752
        if err != nil {
 
753
                t.Fatal(err)
 
754
        }
 
755
 
 
756
        c := Client{Key: testKeyEC, dir: &Directory{CertURL: ts.URL}}
 
757
        cert, certURL, err := c.CreateCert(context.Background(), csrb, notAfter.Sub(notBefore), false)
 
758
        if err != nil {
 
759
                t.Fatal(err)
 
760
        }
 
761
        if cert == nil {
 
762
                t.Errorf("cert is nil")
 
763
        }
 
764
        if certURL != "https://ca.tld/acme/cert/1" {
 
765
                t.Errorf("certURL = %q; want https://ca.tld/acme/cert/1", certURL)
 
766
        }
 
767
}
 
768
 
 
769
func TestFetchCert(t *testing.T) {
 
770
        var count byte
 
771
        var ts *httptest.Server
 
772
        ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
773
                count++
 
774
                if count < 3 {
 
775
                        up := fmt.Sprintf("<%s>;rel=up", ts.URL)
 
776
                        w.Header().Set("link", up)
 
777
                }
 
778
                w.Write([]byte{count})
 
779
        }))
 
780
        defer ts.Close()
 
781
        res, err := (&Client{}).FetchCert(context.Background(), ts.URL, true)
 
782
        if err != nil {
 
783
                t.Fatalf("FetchCert: %v", err)
 
784
        }
 
785
        cert := [][]byte{{1}, {2}, {3}}
 
786
        if !reflect.DeepEqual(res, cert) {
 
787
                t.Errorf("res = %v; want %v", res, cert)
 
788
        }
 
789
}
 
790
 
 
791
func TestFetchCertRetry(t *testing.T) {
 
792
        var count int
 
793
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
794
                if count < 1 {
 
795
                        w.Header().Set("retry-after", "0")
 
796
                        w.WriteHeader(http.StatusAccepted)
 
797
                        count++
 
798
                        return
 
799
                }
 
800
                w.Write([]byte{1})
 
801
        }))
 
802
        defer ts.Close()
 
803
        res, err := (&Client{}).FetchCert(context.Background(), ts.URL, false)
 
804
        if err != nil {
 
805
                t.Fatalf("FetchCert: %v", err)
 
806
        }
 
807
        cert := [][]byte{{1}}
 
808
        if !reflect.DeepEqual(res, cert) {
 
809
                t.Errorf("res = %v; want %v", res, cert)
 
810
        }
 
811
}
 
812
 
 
813
func TestFetchCertCancel(t *testing.T) {
 
814
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
815
                w.Header().Set("retry-after", "0")
 
816
                w.WriteHeader(http.StatusAccepted)
 
817
        }))
 
818
        defer ts.Close()
 
819
        ctx, cancel := context.WithCancel(context.Background())
 
820
        done := make(chan struct{})
 
821
        var err error
 
822
        go func() {
 
823
                _, err = (&Client{}).FetchCert(ctx, ts.URL, false)
 
824
                close(done)
 
825
        }()
 
826
        cancel()
 
827
        <-done
 
828
        if err != context.Canceled {
 
829
                t.Errorf("err = %v; want %v", err, context.Canceled)
 
830
        }
 
831
}
 
832
 
 
833
func TestFetchCertDepth(t *testing.T) {
 
834
        var count byte
 
835
        var ts *httptest.Server
 
836
        ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
837
                count++
 
838
                if count > maxChainLen+1 {
 
839
                        t.Errorf("count = %d; want at most %d", count, maxChainLen+1)
 
840
                        w.WriteHeader(http.StatusInternalServerError)
 
841
                }
 
842
                w.Header().Set("link", fmt.Sprintf("<%s>;rel=up", ts.URL))
 
843
                w.Write([]byte{count})
 
844
        }))
 
845
        defer ts.Close()
 
846
        _, err := (&Client{}).FetchCert(context.Background(), ts.URL, true)
 
847
        if err == nil {
 
848
                t.Errorf("err is nil")
 
849
        }
 
850
}
 
851
 
 
852
func TestFetchCertBreadth(t *testing.T) {
 
853
        var ts *httptest.Server
 
854
        ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
855
                for i := 0; i < maxChainLen+1; i++ {
 
856
                        w.Header().Add("link", fmt.Sprintf("<%s>;rel=up", ts.URL))
 
857
                }
 
858
                w.Write([]byte{1})
 
859
        }))
 
860
        defer ts.Close()
 
861
        _, err := (&Client{}).FetchCert(context.Background(), ts.URL, true)
 
862
        if err == nil {
 
863
                t.Errorf("err is nil")
 
864
        }
 
865
}
 
866
 
 
867
func TestFetchCertSize(t *testing.T) {
 
868
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
869
                b := bytes.Repeat([]byte{1}, maxCertSize+1)
 
870
                w.Write(b)
 
871
        }))
 
872
        defer ts.Close()
 
873
        _, err := (&Client{}).FetchCert(context.Background(), ts.URL, false)
 
874
        if err == nil {
 
875
                t.Errorf("err is nil")
 
876
        }
 
877
}
 
878
 
 
879
func TestRevokeCert(t *testing.T) {
 
880
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
881
                if r.Method == "HEAD" {
 
882
                        w.Header().Set("replay-nonce", "nonce")
 
883
                        return
 
884
                }
 
885
 
 
886
                var req struct {
 
887
                        Resource    string
 
888
                        Certificate string
 
889
                        Reason      int
 
890
                }
 
891
                decodeJWSRequest(t, &req, r)
 
892
                if req.Resource != "revoke-cert" {
 
893
                        t.Errorf("req.Resource = %q; want revoke-cert", req.Resource)
 
894
                }
 
895
                if req.Reason != 1 {
 
896
                        t.Errorf("req.Reason = %d; want 1", req.Reason)
 
897
                }
 
898
                // echo -n cert | base64 | tr -d '=' | tr '/+' '_-'
 
899
                cert := "Y2VydA"
 
900
                if req.Certificate != cert {
 
901
                        t.Errorf("req.Certificate = %q; want %q", req.Certificate, cert)
 
902
                }
 
903
        }))
 
904
        defer ts.Close()
 
905
        client := &Client{
 
906
                Key: testKeyEC,
 
907
                dir: &Directory{RevokeURL: ts.URL},
 
908
        }
 
909
        ctx := context.Background()
 
910
        if err := client.RevokeCert(ctx, nil, []byte("cert"), CRLReasonKeyCompromise); err != nil {
 
911
                t.Fatal(err)
 
912
        }
 
913
}
 
914
 
 
915
func TestFetchNonce(t *testing.T) {
 
916
        tests := []struct {
 
917
                code  int
 
918
                nonce string
 
919
        }{
 
920
                {http.StatusOK, "nonce1"},
 
921
                {http.StatusBadRequest, "nonce2"},
 
922
                {http.StatusOK, ""},
 
923
        }
 
924
        var i int
 
925
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
926
                if r.Method != "HEAD" {
 
927
                        t.Errorf("%d: r.Method = %q; want HEAD", i, r.Method)
 
928
                }
 
929
                w.Header().Set("replay-nonce", tests[i].nonce)
 
930
                w.WriteHeader(tests[i].code)
 
931
        }))
 
932
        defer ts.Close()
 
933
        for ; i < len(tests); i++ {
 
934
                test := tests[i]
 
935
                n, err := fetchNonce(context.Background(), http.DefaultClient, ts.URL)
 
936
                if n != test.nonce {
 
937
                        t.Errorf("%d: n=%q; want %q", i, n, test.nonce)
 
938
                }
 
939
                switch {
 
940
                case err == nil && test.nonce == "":
 
941
                        t.Errorf("%d: n=%q, err=%v; want non-nil error", i, n, err)
 
942
                case err != nil && test.nonce != "":
 
943
                        t.Errorf("%d: n=%q, err=%v; want %q", i, n, err, test.nonce)
 
944
                }
 
945
        }
 
946
}
 
947
 
 
948
func TestLinkHeader(t *testing.T) {
 
949
        h := http.Header{"Link": {
 
950
                `<https://example.com/acme/new-authz>;rel="next"`,
 
951
                `<https://example.com/acme/recover-reg>; rel=recover`,
 
952
                `<https://example.com/acme/terms>; foo=bar; rel="terms-of-service"`,
 
953
                `<dup>;rel="next"`,
 
954
        }}
 
955
        tests := []struct {
 
956
                rel string
 
957
                out []string
 
958
        }{
 
959
                {"next", []string{"https://example.com/acme/new-authz", "dup"}},
 
960
                {"recover", []string{"https://example.com/acme/recover-reg"}},
 
961
                {"terms-of-service", []string{"https://example.com/acme/terms"}},
 
962
                {"empty", nil},
 
963
        }
 
964
        for i, test := range tests {
 
965
                if v := linkHeader(h, test.rel); !reflect.DeepEqual(v, test.out) {
 
966
                        t.Errorf("%d: linkHeader(%q): %v; want %v", i, test.rel, v, test.out)
 
967
                }
 
968
        }
 
969
}
 
970
 
 
971
func TestErrorResponse(t *testing.T) {
 
972
        s := `{
 
973
                "status": 400,
 
974
                "type": "urn:acme:error:xxx",
 
975
                "detail": "text"
 
976
        }`
 
977
        res := &http.Response{
 
978
                StatusCode: 400,
 
979
                Status:     "400 Bad Request",
 
980
                Body:       ioutil.NopCloser(strings.NewReader(s)),
 
981
                Header:     http.Header{"X-Foo": {"bar"}},
 
982
        }
 
983
        err := responseError(res)
 
984
        v, ok := err.(*Error)
 
985
        if !ok {
 
986
                t.Fatalf("err = %+v (%T); want *Error type", err, err)
 
987
        }
 
988
        if v.StatusCode != 400 {
 
989
                t.Errorf("v.StatusCode = %v; want 400", v.StatusCode)
 
990
        }
 
991
        if v.ProblemType != "urn:acme:error:xxx" {
 
992
                t.Errorf("v.ProblemType = %q; want urn:acme:error:xxx", v.ProblemType)
 
993
        }
 
994
        if v.Detail != "text" {
 
995
                t.Errorf("v.Detail = %q; want text", v.Detail)
 
996
        }
 
997
        if !reflect.DeepEqual(v.Header, res.Header) {
 
998
                t.Errorf("v.Header = %+v; want %+v", v.Header, res.Header)
 
999
        }
 
1000
}
 
1001
 
 
1002
func TestTLSSNI01ChallengeCert(t *testing.T) {
 
1003
        const (
 
1004
                token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
 
1005
                // echo -n <token.testKeyECThumbprint> | shasum -a 256
 
1006
                san = "dbbd5eefe7b4d06eb9d1d9f5acb4c7cd.a27d320e4b30332f0b6cb441734ad7b0.acme.invalid"
 
1007
        )
 
1008
 
 
1009
        client := &Client{Key: testKeyEC}
 
1010
        tlscert, name, err := client.TLSSNI01ChallengeCert(token)
 
1011
        if err != nil {
 
1012
                t.Fatal(err)
 
1013
        }
 
1014
 
 
1015
        if n := len(tlscert.Certificate); n != 1 {
 
1016
                t.Fatalf("len(tlscert.Certificate) = %d; want 1", n)
 
1017
        }
 
1018
        cert, err := x509.ParseCertificate(tlscert.Certificate[0])
 
1019
        if err != nil {
 
1020
                t.Fatal(err)
 
1021
        }
 
1022
        if len(cert.DNSNames) != 1 || cert.DNSNames[0] != san {
 
1023
                t.Fatalf("cert.DNSNames = %v; want %q", cert.DNSNames, san)
 
1024
        }
 
1025
        if cert.DNSNames[0] != name {
 
1026
                t.Errorf("cert.DNSNames[0] != name: %q vs %q", cert.DNSNames[0], name)
 
1027
        }
 
1028
}
 
1029
 
 
1030
func TestTLSSNI02ChallengeCert(t *testing.T) {
 
1031
        const (
 
1032
                token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
 
1033
                // echo -n evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA | shasum -a 256
 
1034
                sanA = "7ea0aaa69214e71e02cebb18bb867736.09b730209baabf60e43d4999979ff139.token.acme.invalid"
 
1035
                // echo -n <token.testKeyECThumbprint> | shasum -a 256
 
1036
                sanB = "dbbd5eefe7b4d06eb9d1d9f5acb4c7cd.a27d320e4b30332f0b6cb441734ad7b0.ka.acme.invalid"
 
1037
        )
 
1038
 
 
1039
        client := &Client{Key: testKeyEC}
 
1040
        tlscert, name, err := client.TLSSNI02ChallengeCert(token)
 
1041
        if err != nil {
 
1042
                t.Fatal(err)
 
1043
        }
 
1044
 
 
1045
        if n := len(tlscert.Certificate); n != 1 {
 
1046
                t.Fatalf("len(tlscert.Certificate) = %d; want 1", n)
 
1047
        }
 
1048
        cert, err := x509.ParseCertificate(tlscert.Certificate[0])
 
1049
        if err != nil {
 
1050
                t.Fatal(err)
 
1051
        }
 
1052
        names := []string{sanA, sanB}
 
1053
        if !reflect.DeepEqual(cert.DNSNames, names) {
 
1054
                t.Fatalf("cert.DNSNames = %v;\nwant %v", cert.DNSNames, names)
 
1055
        }
 
1056
        sort.Strings(cert.DNSNames)
 
1057
        i := sort.SearchStrings(cert.DNSNames, name)
 
1058
        if i >= len(cert.DNSNames) || cert.DNSNames[i] != name {
 
1059
                t.Errorf("%v doesn't have %q", cert.DNSNames, name)
 
1060
        }
 
1061
}
 
1062
 
 
1063
func TestTLSChallengeCertOpt(t *testing.T) {
 
1064
        key, err := rsa.GenerateKey(rand.Reader, 512)
 
1065
        if err != nil {
 
1066
                t.Fatal(err)
 
1067
        }
 
1068
        tmpl := &x509.Certificate{
 
1069
                SerialNumber: big.NewInt(2),
 
1070
                Subject:      pkix.Name{Organization: []string{"Test"}},
 
1071
                DNSNames:     []string{"should-be-overwritten"},
 
1072
        }
 
1073
        opts := []CertOption{WithKey(key), WithTemplate(tmpl)}
 
1074
 
 
1075
        client := &Client{Key: testKeyEC}
 
1076
        cert1, _, err := client.TLSSNI01ChallengeCert("token", opts...)
 
1077
        if err != nil {
 
1078
                t.Fatal(err)
 
1079
        }
 
1080
        cert2, _, err := client.TLSSNI02ChallengeCert("token", opts...)
 
1081
        if err != nil {
 
1082
                t.Fatal(err)
 
1083
        }
 
1084
 
 
1085
        for i, tlscert := range []tls.Certificate{cert1, cert2} {
 
1086
                // verify generated cert private key
 
1087
                tlskey, ok := tlscert.PrivateKey.(*rsa.PrivateKey)
 
1088
                if !ok {
 
1089
                        t.Errorf("%d: tlscert.PrivateKey is %T; want *rsa.PrivateKey", i, tlscert.PrivateKey)
 
1090
                        continue
 
1091
                }
 
1092
                if tlskey.D.Cmp(key.D) != 0 {
 
1093
                        t.Errorf("%d: tlskey.D = %v; want %v", i, tlskey.D, key.D)
 
1094
                }
 
1095
                // verify generated cert public key
 
1096
                x509Cert, err := x509.ParseCertificate(tlscert.Certificate[0])
 
1097
                if err != nil {
 
1098
                        t.Errorf("%d: %v", i, err)
 
1099
                        continue
 
1100
                }
 
1101
                tlspub, ok := x509Cert.PublicKey.(*rsa.PublicKey)
 
1102
                if !ok {
 
1103
                        t.Errorf("%d: x509Cert.PublicKey is %T; want *rsa.PublicKey", i, x509Cert.PublicKey)
 
1104
                        continue
 
1105
                }
 
1106
                if tlspub.N.Cmp(key.N) != 0 {
 
1107
                        t.Errorf("%d: tlspub.N = %v; want %v", i, tlspub.N, key.N)
 
1108
                }
 
1109
                // verify template option
 
1110
                sn := big.NewInt(2)
 
1111
                if x509Cert.SerialNumber.Cmp(sn) != 0 {
 
1112
                        t.Errorf("%d: SerialNumber = %v; want %v", i, x509Cert.SerialNumber, sn)
 
1113
                }
 
1114
                org := []string{"Test"}
 
1115
                if !reflect.DeepEqual(x509Cert.Subject.Organization, org) {
 
1116
                        t.Errorf("%d: Subject.Organization = %+v; want %+v", i, x509Cert.Subject.Organization, org)
 
1117
                }
 
1118
                for _, v := range x509Cert.DNSNames {
 
1119
                        if !strings.HasSuffix(v, ".acme.invalid") {
 
1120
                                t.Errorf("%d: invalid DNSNames element: %q", i, v)
 
1121
                        }
 
1122
                }
 
1123
        }
 
1124
}
 
1125
 
 
1126
func TestHTTP01Challenge(t *testing.T) {
 
1127
        const (
 
1128
                token = "xxx"
 
1129
                // thumbprint is precomputed for testKeyEC in jws_test.go
 
1130
                value   = token + "." + testKeyECThumbprint
 
1131
                urlpath = "/.well-known/acme-challenge/" + token
 
1132
        )
 
1133
        client := &Client{Key: testKeyEC}
 
1134
        val, err := client.HTTP01ChallengeResponse(token)
 
1135
        if err != nil {
 
1136
                t.Fatal(err)
 
1137
        }
 
1138
        if val != value {
 
1139
                t.Errorf("val = %q; want %q", val, value)
 
1140
        }
 
1141
        if path := client.HTTP01ChallengePath(token); path != urlpath {
 
1142
                t.Errorf("path = %q; want %q", path, urlpath)
 
1143
        }
 
1144
}
 
1145
 
 
1146
func TestDNS01ChallengeRecord(t *testing.T) {
 
1147
        // echo -n xxx.<testKeyECThumbprint> | \
 
1148
        //      openssl dgst -binary -sha256 | \
 
1149
        //      base64 | tr -d '=' | tr '/+' '_-'
 
1150
        const value = "8DERMexQ5VcdJ_prpPiA0mVdp7imgbCgjsG4SqqNMIo"
 
1151
 
 
1152
        client := &Client{Key: testKeyEC}
 
1153
        val, err := client.DNS01ChallengeRecord("xxx")
 
1154
        if err != nil {
 
1155
                t.Fatal(err)
 
1156
        }
 
1157
        if val != value {
 
1158
                t.Errorf("val = %q; want %q", val, value)
 
1159
        }
 
1160
}
 
1161
 
 
1162
func TestBackoff(t *testing.T) {
 
1163
        tt := []struct{ min, max time.Duration }{
 
1164
                {time.Second, 2 * time.Second},
 
1165
                {2 * time.Second, 3 * time.Second},
 
1166
                {4 * time.Second, 5 * time.Second},
 
1167
                {8 * time.Second, 9 * time.Second},
 
1168
        }
 
1169
        for i, test := range tt {
 
1170
                d := backoff(i, time.Minute)
 
1171
                if d < test.min || test.max < d {
 
1172
                        t.Errorf("%d: d = %v; want between %v and %v", i, d, test.min, test.max)
 
1173
                }
 
1174
        }
 
1175
 
 
1176
        min, max := time.Second, 2*time.Second
 
1177
        if d := backoff(-1, time.Minute); d < min || max < d {
 
1178
                t.Errorf("d = %v; want between %v and %v", d, min, max)
 
1179
        }
 
1180
 
 
1181
        bound := 10 * time.Second
 
1182
        if d := backoff(100, bound); d != bound {
 
1183
                t.Errorf("d = %v; want %v", d, bound)
 
1184
        }
 
1185
}