1
2
3
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/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
31
32
33 func newTestClient() *Client {
34 return &Client{
35 Key: testKeyEC,
36 dir: &Directory{},
37 }
38 }
39
40
41
42 func decodeJWSRequest(t *testing.T, v interface{}, r io.Reader) {
43
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
58 type 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
66 func 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
82 func TestRegisterWithoutKey(t *testing.T) {
83 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
84 if r.Method == "HEAD" {
85 w.Header().Set("Replay-Nonce", "test-nonce")
86 return
87 }
88 w.WriteHeader(http.StatusCreated)
89 fmt.Fprint(w, `{}`)
90 }))
91 defer ts.Close()
92
93 c := Client{
94 Key: testKeyEC,
95 DirectoryURL: ts.URL,
96 dir: &Directory{RegURL: ts.URL},
97 }
98 if _, err := c.Register(context.Background(), &Account{}, AcceptTOS); err != nil {
99 t.Fatalf("c.Register() = %v; want success with a complete test client", err)
100 }
101 c.Key = nil
102 if _, err := c.Register(context.Background(), &Account{}, AcceptTOS); err == nil {
103 t.Error("c.Register() from client without key succeeded, wanted error")
104 }
105 }
106
107 func TestAuthorize(t *testing.T) {
108 tt := []struct{ typ, value string }{
109 {"dns", "example.com"},
110 {"ip", "1.2.3.4"},
111 }
112 for _, test := range tt {
113 t.Run(test.typ, func(t *testing.T) {
114 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
115 if r.Method == "HEAD" {
116 w.Header().Set("Replay-Nonce", "test-nonce")
117 return
118 }
119 if r.Method != "POST" {
120 t.Errorf("r.Method = %q; want POST", r.Method)
121 }
122
123 var j struct {
124 Resource string
125 Identifier struct {
126 Type string
127 Value string
128 }
129 }
130 decodeJWSRequest(t, &j, r.Body)
131
132
133 if j.Resource != "new-authz" {
134 t.Errorf("j.Resource = %q; want new-authz", j.Resource)
135 }
136 if j.Identifier.Type != test.typ {
137 t.Errorf("j.Identifier.Type = %q; want %q", j.Identifier.Type, test.typ)
138 }
139 if j.Identifier.Value != test.value {
140 t.Errorf("j.Identifier.Value = %q; want %q", j.Identifier.Value, test.value)
141 }
142
143 w.Header().Set("Location", "https://ca.tld/acme/auth/1")
144 w.WriteHeader(http.StatusCreated)
145 fmt.Fprintf(w, `{
146 "identifier": {"type":%q,"value":%q},
147 "status":"pending",
148 "challenges":[
149 {
150 "type":"http-01",
151 "status":"pending",
152 "uri":"https://ca.tld/acme/challenge/publickey/id1",
153 "token":"token1"
154 },
155 {
156 "type":"tls-sni-01",
157 "status":"pending",
158 "uri":"https://ca.tld/acme/challenge/publickey/id2",
159 "token":"token2"
160 }
161 ],
162 "combinations":[[0],[1]]
163 }`, test.typ, test.value)
164 }))
165 defer ts.Close()
166
167 var (
168 auth *Authorization
169 err error
170 )
171 cl := Client{
172 Key: testKeyEC,
173 DirectoryURL: ts.URL,
174 dir: &Directory{AuthzURL: ts.URL},
175 }
176 switch test.typ {
177 case "dns":
178 auth, err = cl.Authorize(context.Background(), test.value)
179 case "ip":
180 auth, err = cl.AuthorizeIP(context.Background(), test.value)
181 default:
182 t.Fatalf("unknown identifier type: %q", test.typ)
183 }
184 if err != nil {
185 t.Fatal(err)
186 }
187
188 if auth.URI != "https://ca.tld/acme/auth/1" {
189 t.Errorf("URI = %q; want https://ca.tld/acme/auth/1", auth.URI)
190 }
191 if auth.Status != "pending" {
192 t.Errorf("Status = %q; want pending", auth.Status)
193 }
194 if auth.Identifier.Type != test.typ {
195 t.Errorf("Identifier.Type = %q; want %q", auth.Identifier.Type, test.typ)
196 }
197 if auth.Identifier.Value != test.value {
198 t.Errorf("Identifier.Value = %q; want %q", auth.Identifier.Value, test.value)
199 }
200
201 if n := len(auth.Challenges); n != 2 {
202 t.Fatalf("len(auth.Challenges) = %d; want 2", n)
203 }
204
205 c := auth.Challenges[0]
206 if c.Type != "http-01" {
207 t.Errorf("c.Type = %q; want http-01", c.Type)
208 }
209 if c.URI != "https://ca.tld/acme/challenge/publickey/id1" {
210 t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", c.URI)
211 }
212 if c.Token != "token1" {
213 t.Errorf("c.Token = %q; want token1", c.Token)
214 }
215
216 c = auth.Challenges[1]
217 if c.Type != "tls-sni-01" {
218 t.Errorf("c.Type = %q; want tls-sni-01", c.Type)
219 }
220 if c.URI != "https://ca.tld/acme/challenge/publickey/id2" {
221 t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id2", c.URI)
222 }
223 if c.Token != "token2" {
224 t.Errorf("c.Token = %q; want token2", c.Token)
225 }
226
227 combs := [][]int{{0}, {1}}
228 if !reflect.DeepEqual(auth.Combinations, combs) {
229 t.Errorf("auth.Combinations: %+v\nwant: %+v\n", auth.Combinations, combs)
230 }
231
232 })
233 }
234 }
235
236 func TestAuthorizeValid(t *testing.T) {
237 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
238 if r.Method == "HEAD" {
239 w.Header().Set("Replay-Nonce", "nonce")
240 return
241 }
242 w.WriteHeader(http.StatusCreated)
243 w.Write([]byte(`{"status":"valid"}`))
244 }))
245 defer ts.Close()
246 client := Client{
247 Key: testKey,
248 DirectoryURL: ts.URL,
249 dir: &Directory{AuthzURL: ts.URL},
250 }
251 _, err := client.Authorize(context.Background(), "example.com")
252 if err != nil {
253 t.Errorf("err = %v", err)
254 }
255 }
256
257 func TestWaitAuthorization(t *testing.T) {
258 t.Run("wait loop", func(t *testing.T) {
259 var count int
260 authz, err := runWaitAuthorization(context.Background(), t, func(w http.ResponseWriter, r *http.Request) {
261 count++
262 w.Header().Set("Retry-After", "0")
263 if count > 1 {
264 fmt.Fprintf(w, `{"status":"valid"}`)
265 return
266 }
267 fmt.Fprintf(w, `{"status":"pending"}`)
268 })
269 if err != nil {
270 t.Fatalf("non-nil error: %v", err)
271 }
272 if authz == nil {
273 t.Fatal("authz is nil")
274 }
275 })
276 t.Run("invalid status", func(t *testing.T) {
277 _, err := runWaitAuthorization(context.Background(), t, func(w http.ResponseWriter, r *http.Request) {
278 fmt.Fprintf(w, `{"status":"invalid"}`)
279 })
280 if _, ok := err.(*AuthorizationError); !ok {
281 t.Errorf("err is %v (%T); want non-nil *AuthorizationError", err, err)
282 }
283 })
284 t.Run("invalid status with error returns the authorization error", func(t *testing.T) {
285 _, err := runWaitAuthorization(context.Background(), t, func(w http.ResponseWriter, r *http.Request) {
286 fmt.Fprintf(w, `{
287 "type": "dns-01",
288 "status": "invalid",
289 "error": {
290 "type": "urn:ietf:params:acme:error:caa",
291 "detail": "CAA record for <domain> prevents issuance",
292 "status": 403
293 },
294 "url": "https://acme-v02.api.letsencrypt.org/acme/chall-v3/xxx/xxx",
295 "token": "xxx",
296 "validationRecord": [
297 {
298 "hostname": "<domain>"
299 }
300 ]
301 }`)
302 })
303
304 want := &AuthorizationError{
305 Errors: []error{
306 (&wireError{
307 Status: 403,
308 Type: "urn:ietf:params:acme:error:caa",
309 Detail: "CAA record for <domain> prevents issuance",
310 }).error(nil),
311 },
312 }
313
314 _, ok := err.(*AuthorizationError)
315 if !ok {
316 t.Errorf("err is %T; want non-nil *AuthorizationError", err)
317 }
318
319 if err.Error() != want.Error() {
320 t.Errorf("err is %v; want %v", err, want)
321 }
322 })
323 t.Run("non-retriable error", func(t *testing.T) {
324 const code = http.StatusBadRequest
325 _, err := runWaitAuthorization(context.Background(), t, func(w http.ResponseWriter, r *http.Request) {
326 w.WriteHeader(code)
327 })
328 res, ok := err.(*Error)
329 if !ok {
330 t.Fatalf("err is %v (%T); want a non-nil *Error", err, err)
331 }
332 if res.StatusCode != code {
333 t.Errorf("res.StatusCode = %d; want %d", res.StatusCode, code)
334 }
335 })
336 for _, code := range []int{http.StatusTooManyRequests, http.StatusInternalServerError} {
337 t.Run(fmt.Sprintf("retriable %d error", code), func(t *testing.T) {
338 var count int
339 authz, err := runWaitAuthorization(context.Background(), t, func(w http.ResponseWriter, r *http.Request) {
340 count++
341 w.Header().Set("Retry-After", "0")
342 if count > 1 {
343 fmt.Fprintf(w, `{"status":"valid"}`)
344 return
345 }
346 w.WriteHeader(code)
347 })
348 if err != nil {
349 t.Fatalf("non-nil error: %v", err)
350 }
351 if authz == nil {
352 t.Fatal("authz is nil")
353 }
354 })
355 }
356 t.Run("context cancel", func(t *testing.T) {
357 ctx, cancel := context.WithCancel(context.Background())
358 defer cancel()
359 _, err := runWaitAuthorization(ctx, t, func(w http.ResponseWriter, r *http.Request) {
360 w.Header().Set("Retry-After", "60")
361 fmt.Fprintf(w, `{"status":"pending"}`)
362 time.AfterFunc(1*time.Millisecond, cancel)
363 })
364 if err == nil {
365 t.Error("err is nil")
366 }
367 })
368 }
369
370 func runWaitAuthorization(ctx context.Context, t *testing.T, h http.HandlerFunc) (*Authorization, error) {
371 t.Helper()
372 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
373 w.Header().Set("Replay-Nonce", fmt.Sprintf("bad-test-nonce-%v", time.Now().UnixNano()))
374 h(w, r)
375 }))
376 defer ts.Close()
377
378 client := &Client{
379 Key: testKey,
380 DirectoryURL: ts.URL,
381 dir: &Directory{},
382 KID: "some-key-id",
383 }
384 return client.WaitAuthorization(ctx, ts.URL)
385 }
386
387 func TestRevokeAuthorization(t *testing.T) {
388 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
389 if r.Method == "HEAD" {
390 w.Header().Set("Replay-Nonce", "nonce")
391 return
392 }
393 switch r.URL.Path {
394 case "/1":
395 var req struct {
396 Resource string
397 Status string
398 Delete bool
399 }
400 decodeJWSRequest(t, &req, r.Body)
401 if req.Resource != "authz" {
402 t.Errorf("req.Resource = %q; want authz", req.Resource)
403 }
404 if req.Status != "deactivated" {
405 t.Errorf("req.Status = %q; want deactivated", req.Status)
406 }
407 if !req.Delete {
408 t.Errorf("req.Delete is false")
409 }
410 case "/2":
411 w.WriteHeader(http.StatusBadRequest)
412 }
413 }))
414 defer ts.Close()
415 client := &Client{
416 Key: testKey,
417 DirectoryURL: ts.URL,
418 dir: &Directory{},
419 }
420 ctx := context.Background()
421 if err := client.RevokeAuthorization(ctx, ts.URL+"/1"); err != nil {
422 t.Errorf("err = %v", err)
423 }
424 if client.RevokeAuthorization(ctx, ts.URL+"/2") == nil {
425 t.Error("nil error")
426 }
427 }
428
429 func TestFetchCertCancel(t *testing.T) {
430 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
431 <-r.Context().Done()
432 w.Header().Set("Retry-After", "0")
433 w.WriteHeader(http.StatusBadRequest)
434 }))
435 defer ts.Close()
436 ctx, cancel := context.WithCancel(context.Background())
437 done := make(chan struct{})
438 var err error
439 go func() {
440 cl := newTestClient()
441 _, err = cl.FetchCert(ctx, ts.URL, false)
442 close(done)
443 }()
444 cancel()
445 <-done
446 if err != context.Canceled {
447 t.Errorf("err = %v; want %v", err, context.Canceled)
448 }
449 }
450
451 func TestFetchCertDepth(t *testing.T) {
452 var count byte
453 var ts *httptest.Server
454 ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
455 count++
456 if count > maxChainLen+1 {
457 t.Errorf("count = %d; want at most %d", count, maxChainLen+1)
458 w.WriteHeader(http.StatusInternalServerError)
459 }
460 w.Header().Set("Link", fmt.Sprintf("<%s>;rel=up", ts.URL))
461 w.Write([]byte{count})
462 }))
463 defer ts.Close()
464 cl := newTestClient()
465 _, err := cl.FetchCert(context.Background(), ts.URL, true)
466 if err == nil {
467 t.Errorf("err is nil")
468 }
469 }
470
471 func TestFetchCertBreadth(t *testing.T) {
472 var ts *httptest.Server
473 ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
474 for i := 0; i < maxChainLen+1; i++ {
475 w.Header().Add("Link", fmt.Sprintf("<%s>;rel=up", ts.URL))
476 }
477 w.Write([]byte{1})
478 }))
479 defer ts.Close()
480 cl := newTestClient()
481 _, err := cl.FetchCert(context.Background(), ts.URL, true)
482 if err == nil {
483 t.Errorf("err is nil")
484 }
485 }
486
487 func TestFetchCertSize(t *testing.T) {
488 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
489 b := bytes.Repeat([]byte{1}, maxCertSize+1)
490 w.Write(b)
491 }))
492 defer ts.Close()
493 cl := newTestClient()
494 _, err := cl.FetchCert(context.Background(), ts.URL, false)
495 if err == nil {
496 t.Errorf("err is nil")
497 }
498 }
499
500 func TestNonce_add(t *testing.T) {
501 var c Client
502 c.addNonce(http.Header{"Replay-Nonce": {"nonce"}})
503 c.addNonce(http.Header{"Replay-Nonce": {}})
504 c.addNonce(http.Header{"Replay-Nonce": {"nonce"}})
505
506 nonces := map[string]struct{}{"nonce": {}}
507 if !reflect.DeepEqual(c.nonces, nonces) {
508 t.Errorf("c.nonces = %q; want %q", c.nonces, nonces)
509 }
510 }
511
512 func TestNonce_addMax(t *testing.T) {
513 c := &Client{nonces: make(map[string]struct{})}
514 for i := 0; i < maxNonces; i++ {
515 c.nonces[fmt.Sprintf("%d", i)] = struct{}{}
516 }
517 c.addNonce(http.Header{"Replay-Nonce": {"nonce"}})
518 if n := len(c.nonces); n != maxNonces {
519 t.Errorf("len(c.nonces) = %d; want %d", n, maxNonces)
520 }
521 }
522
523 func TestNonce_fetch(t *testing.T) {
524 tests := []struct {
525 code int
526 nonce string
527 }{
528 {http.StatusOK, "nonce1"},
529 {http.StatusBadRequest, "nonce2"},
530 {http.StatusOK, ""},
531 }
532 var i int
533 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
534 if r.Method != "HEAD" {
535 t.Errorf("%d: r.Method = %q; want HEAD", i, r.Method)
536 }
537 w.Header().Set("Replay-Nonce", tests[i].nonce)
538 w.WriteHeader(tests[i].code)
539 }))
540 defer ts.Close()
541 for ; i < len(tests); i++ {
542 test := tests[i]
543 c := newTestClient()
544 n, err := c.fetchNonce(context.Background(), ts.URL)
545 if n != test.nonce {
546 t.Errorf("%d: n=%q; want %q", i, n, test.nonce)
547 }
548 switch {
549 case err == nil && test.nonce == "":
550 t.Errorf("%d: n=%q, err=%v; want non-nil error", i, n, err)
551 case err != nil && test.nonce != "":
552 t.Errorf("%d: n=%q, err=%v; want %q", i, n, err, test.nonce)
553 }
554 }
555 }
556
557 func TestNonce_fetchError(t *testing.T) {
558 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
559 w.WriteHeader(http.StatusTooManyRequests)
560 }))
561 defer ts.Close()
562 c := newTestClient()
563 _, err := c.fetchNonce(context.Background(), ts.URL)
564 e, ok := err.(*Error)
565 if !ok {
566 t.Fatalf("err is %T; want *Error", err)
567 }
568 if e.StatusCode != http.StatusTooManyRequests {
569 t.Errorf("e.StatusCode = %d; want %d", e.StatusCode, http.StatusTooManyRequests)
570 }
571 }
572
573 func TestNonce_popWhenEmpty(t *testing.T) {
574 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
575 if r.Method != "HEAD" {
576 t.Errorf("r.Method = %q; want HEAD", r.Method)
577 }
578 switch r.URL.Path {
579 case "/dir-with-nonce":
580 w.Header().Set("Replay-Nonce", "dirnonce")
581 case "/new-nonce":
582 w.Header().Set("Replay-Nonce", "newnonce")
583 case "/dir-no-nonce", "/empty":
584
585 default:
586 t.Errorf("Unknown URL: %s", r.URL)
587 }
588 }))
589 defer ts.Close()
590 ctx := context.Background()
591
592 tt := []struct {
593 dirURL, popURL, nonce string
594 wantOK bool
595 }{
596 {ts.URL + "/dir-with-nonce", ts.URL + "/new-nonce", "dirnonce", true},
597 {ts.URL + "/dir-no-nonce", ts.URL + "/new-nonce", "newnonce", true},
598 {ts.URL + "/dir-no-nonce", ts.URL + "/empty", "", false},
599 }
600 for _, test := range tt {
601 t.Run(fmt.Sprintf("nonce:%s wantOK:%v", test.nonce, test.wantOK), func(t *testing.T) {
602 c := Client{DirectoryURL: test.dirURL}
603 v, err := c.popNonce(ctx, test.popURL)
604 if !test.wantOK {
605 if err == nil {
606 t.Fatalf("c.popNonce(%q) returned nil error", test.popURL)
607 }
608 return
609 }
610 if err != nil {
611 t.Fatalf("c.popNonce(%q): %v", test.popURL, err)
612 }
613 if v != test.nonce {
614 t.Errorf("c.popNonce(%q) = %q; want %q", test.popURL, v, test.nonce)
615 }
616 })
617 }
618 }
619
620 func TestLinkHeader(t *testing.T) {
621 h := http.Header{"Link": {
622 `<https://example.com/acme/new-authz>;rel="next"`,
623 `<https://example.com/acme/recover-reg>; rel=recover`,
624 `<https://example.com/acme/terms>; foo=bar; rel="terms-of-service"`,
625 `<dup>;rel="next"`,
626 }}
627 tests := []struct {
628 rel string
629 out []string
630 }{
631 {"next", []string{"https://example.com/acme/new-authz", "dup"}},
632 {"recover", []string{"https://example.com/acme/recover-reg"}},
633 {"terms-of-service", []string{"https://example.com/acme/terms"}},
634 {"empty", nil},
635 }
636 for i, test := range tests {
637 if v := linkHeader(h, test.rel); !reflect.DeepEqual(v, test.out) {
638 t.Errorf("%d: linkHeader(%q): %v; want %v", i, test.rel, v, test.out)
639 }
640 }
641 }
642
643 func TestTLSSNI01ChallengeCert(t *testing.T) {
644 const (
645 token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
646
647 san = "dbbd5eefe7b4d06eb9d1d9f5acb4c7cd.a27d320e4b30332f0b6cb441734ad7b0.acme.invalid"
648 )
649
650 tlscert, name, err := newTestClient().TLSSNI01ChallengeCert(token)
651 if err != nil {
652 t.Fatal(err)
653 }
654
655 if n := len(tlscert.Certificate); n != 1 {
656 t.Fatalf("len(tlscert.Certificate) = %d; want 1", n)
657 }
658 cert, err := x509.ParseCertificate(tlscert.Certificate[0])
659 if err != nil {
660 t.Fatal(err)
661 }
662 if len(cert.DNSNames) != 1 || cert.DNSNames[0] != san {
663 t.Fatalf("cert.DNSNames = %v; want %q", cert.DNSNames, san)
664 }
665 if cert.DNSNames[0] != name {
666 t.Errorf("cert.DNSNames[0] != name: %q vs %q", cert.DNSNames[0], name)
667 }
668 if cn := cert.Subject.CommonName; cn != san {
669 t.Errorf("cert.Subject.CommonName = %q; want %q", cn, san)
670 }
671 }
672
673 func TestTLSSNI02ChallengeCert(t *testing.T) {
674 const (
675 token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
676
677 sanA = "7ea0aaa69214e71e02cebb18bb867736.09b730209baabf60e43d4999979ff139.token.acme.invalid"
678
679 sanB = "dbbd5eefe7b4d06eb9d1d9f5acb4c7cd.a27d320e4b30332f0b6cb441734ad7b0.ka.acme.invalid"
680 )
681
682 tlscert, name, err := newTestClient().TLSSNI02ChallengeCert(token)
683 if err != nil {
684 t.Fatal(err)
685 }
686
687 if n := len(tlscert.Certificate); n != 1 {
688 t.Fatalf("len(tlscert.Certificate) = %d; want 1", n)
689 }
690 cert, err := x509.ParseCertificate(tlscert.Certificate[0])
691 if err != nil {
692 t.Fatal(err)
693 }
694 names := []string{sanA, sanB}
695 if !reflect.DeepEqual(cert.DNSNames, names) {
696 t.Fatalf("cert.DNSNames = %v;\nwant %v", cert.DNSNames, names)
697 }
698 sort.Strings(cert.DNSNames)
699 i := sort.SearchStrings(cert.DNSNames, name)
700 if i >= len(cert.DNSNames) || cert.DNSNames[i] != name {
701 t.Errorf("%v doesn't have %q", cert.DNSNames, name)
702 }
703 if cn := cert.Subject.CommonName; cn != sanA {
704 t.Errorf("CommonName = %q; want %q", cn, sanA)
705 }
706 }
707
708 func TestTLSALPN01ChallengeCert(t *testing.T) {
709 const (
710 token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
711 keyAuth = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA." + testKeyECThumbprint
712
713 h = "0420dbbd5eefe7b4d06eb9d1d9f5acb4c7cda27d320e4b30332f0b6cb441734ad7b0"
714 domain = "example.com"
715 )
716
717 extValue, err := hex.DecodeString(h)
718 if err != nil {
719 t.Fatal(err)
720 }
721
722 tlscert, err := newTestClient().TLSALPN01ChallengeCert(token, domain)
723 if err != nil {
724 t.Fatal(err)
725 }
726
727 if n := len(tlscert.Certificate); n != 1 {
728 t.Fatalf("len(tlscert.Certificate) = %d; want 1", n)
729 }
730 cert, err := x509.ParseCertificate(tlscert.Certificate[0])
731 if err != nil {
732 t.Fatal(err)
733 }
734 names := []string{domain}
735 if !reflect.DeepEqual(cert.DNSNames, names) {
736 t.Fatalf("cert.DNSNames = %v;\nwant %v", cert.DNSNames, names)
737 }
738 if cn := cert.Subject.CommonName; cn != domain {
739 t.Errorf("CommonName = %q; want %q", cn, domain)
740 }
741 acmeExts := []pkix.Extension{}
742 for _, ext := range cert.Extensions {
743 if idPeACMEIdentifier.Equal(ext.Id) {
744 acmeExts = append(acmeExts, ext)
745 }
746 }
747 if len(acmeExts) != 1 {
748 t.Errorf("acmeExts = %v; want exactly one", acmeExts)
749 }
750 if !acmeExts[0].Critical {
751 t.Errorf("acmeExt.Critical = %v; want true", acmeExts[0].Critical)
752 }
753 if bytes.Compare(acmeExts[0].Value, extValue) != 0 {
754 t.Errorf("acmeExt.Value = %v; want %v", acmeExts[0].Value, extValue)
755 }
756
757 }
758
759 func TestTLSChallengeCertOpt(t *testing.T) {
760 key, err := rsa.GenerateKey(rand.Reader, 512)
761 if err != nil {
762 t.Fatal(err)
763 }
764 tmpl := &x509.Certificate{
765 SerialNumber: big.NewInt(2),
766 Subject: pkix.Name{Organization: []string{"Test"}},
767 DNSNames: []string{"should-be-overwritten"},
768 }
769 opts := []CertOption{WithKey(key), WithTemplate(tmpl)}
770
771 client := newTestClient()
772 cert1, _, err := client.TLSSNI01ChallengeCert("token", opts...)
773 if err != nil {
774 t.Fatal(err)
775 }
776 cert2, _, err := client.TLSSNI02ChallengeCert("token", opts...)
777 if err != nil {
778 t.Fatal(err)
779 }
780
781 for i, tlscert := range []tls.Certificate{cert1, cert2} {
782
783 tlskey, ok := tlscert.PrivateKey.(*rsa.PrivateKey)
784 if !ok {
785 t.Errorf("%d: tlscert.PrivateKey is %T; want *rsa.PrivateKey", i, tlscert.PrivateKey)
786 continue
787 }
788 if tlskey.D.Cmp(key.D) != 0 {
789 t.Errorf("%d: tlskey.D = %v; want %v", i, tlskey.D, key.D)
790 }
791
792 x509Cert, err := x509.ParseCertificate(tlscert.Certificate[0])
793 if err != nil {
794 t.Errorf("%d: %v", i, err)
795 continue
796 }
797 tlspub, ok := x509Cert.PublicKey.(*rsa.PublicKey)
798 if !ok {
799 t.Errorf("%d: x509Cert.PublicKey is %T; want *rsa.PublicKey", i, x509Cert.PublicKey)
800 continue
801 }
802 if tlspub.N.Cmp(key.N) != 0 {
803 t.Errorf("%d: tlspub.N = %v; want %v", i, tlspub.N, key.N)
804 }
805
806 sn := big.NewInt(2)
807 if x509Cert.SerialNumber.Cmp(sn) != 0 {
808 t.Errorf("%d: SerialNumber = %v; want %v", i, x509Cert.SerialNumber, sn)
809 }
810 org := []string{"Test"}
811 if !reflect.DeepEqual(x509Cert.Subject.Organization, org) {
812 t.Errorf("%d: Subject.Organization = %+v; want %+v", i, x509Cert.Subject.Organization, org)
813 }
814 for _, v := range x509Cert.DNSNames {
815 if !strings.HasSuffix(v, ".acme.invalid") {
816 t.Errorf("%d: invalid DNSNames element: %q", i, v)
817 }
818 }
819 }
820 }
821
822 func TestHTTP01Challenge(t *testing.T) {
823 const (
824 token = "xxx"
825
826 value = token + "." + testKeyECThumbprint
827 urlpath = "/.well-known/acme-challenge/" + token
828 )
829 client := newTestClient()
830 val, err := client.HTTP01ChallengeResponse(token)
831 if err != nil {
832 t.Fatal(err)
833 }
834 if val != value {
835 t.Errorf("val = %q; want %q", val, value)
836 }
837 if path := client.HTTP01ChallengePath(token); path != urlpath {
838 t.Errorf("path = %q; want %q", path, urlpath)
839 }
840 }
841
842 func TestDNS01ChallengeRecord(t *testing.T) {
843
844
845
846 const value = "8DERMexQ5VcdJ_prpPiA0mVdp7imgbCgjsG4SqqNMIo"
847
848 val, err := newTestClient().DNS01ChallengeRecord("xxx")
849 if err != nil {
850 t.Fatal(err)
851 }
852 if val != value {
853 t.Errorf("val = %q; want %q", val, value)
854 }
855 }
856
View as plain text