OSDN Git Service

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