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