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.
27
"golang.org/x/net/context"
30
// Decodes a JWS-encoded request and unmarshals the decoded JSON into a provided
32
func decodeJWSRequest(t *testing.T, v interface{}, r *http.Request) {
34
var req struct{ Payload string }
35
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
38
payload, err := base64.RawURLEncoding.DecodeString(req.Payload)
42
err = json.Unmarshal(payload, v)
48
func TestDiscover(t *testing.T) {
50
reg = "https://example.com/acme/new-reg"
51
authz = "https://example.com/acme/new-authz"
52
cert = "https://example.com/acme/new-cert"
53
revoke = "https://example.com/acme/revoke-cert"
55
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
56
w.Header().Set("content-type", "application/json")
62
}`, reg, authz, cert, revoke)
65
c := Client{DirectoryURL: ts.URL}
66
dir, err := c.Discover(context.Background())
70
if dir.RegURL != reg {
71
t.Errorf("dir.RegURL = %q; want %q", dir.RegURL, reg)
73
if dir.AuthzURL != authz {
74
t.Errorf("dir.AuthzURL = %q; want %q", dir.AuthzURL, authz)
76
if dir.CertURL != cert {
77
t.Errorf("dir.CertURL = %q; want %q", dir.CertURL, cert)
79
if dir.RevokeURL != revoke {
80
t.Errorf("dir.RevokeURL = %q; want %q", dir.RevokeURL, revoke)
84
func TestRegister(t *testing.T) {
85
contacts := []string{"mailto:admin@example.com"}
87
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
88
if r.Method == "HEAD" {
89
w.Header().Set("replay-nonce", "test-nonce")
92
if r.Method != "POST" {
93
t.Errorf("r.Method = %q; want POST", r.Method)
101
decodeJWSRequest(t, &j, r)
104
if j.Resource != "new-reg" {
105
t.Errorf("j.Resource = %q; want new-reg", j.Resource)
107
if !reflect.DeepEqual(j.Contact, contacts) {
108
t.Errorf("j.Contact = %v; want %v", j.Contact, contacts)
111
w.Header().Set("Location", "https://ca.tld/acme/reg/1")
112
w.Header().Set("Link", `<https://ca.tld/acme/new-authz>;rel="next"`)
113
w.Header().Add("Link", `<https://ca.tld/acme/recover-reg>;rel="recover"`)
114
w.Header().Add("Link", `<https://ca.tld/acme/terms>;rel="terms-of-service"`)
115
w.WriteHeader(http.StatusCreated)
116
b, _ := json.Marshal(contacts)
117
fmt.Fprintf(w, `{"contact": %s}`, b)
121
prompt := func(url string) bool {
122
const terms = "https://ca.tld/acme/terms"
124
t.Errorf("prompt url = %q; want %q", url, terms)
129
c := Client{Key: testKeyEC, dir: &Directory{RegURL: ts.URL}}
130
a := &Account{Contact: contacts}
132
if a, err = c.Register(context.Background(), a, prompt); err != nil {
135
if a.URI != "https://ca.tld/acme/reg/1" {
136
t.Errorf("a.URI = %q; want https://ca.tld/acme/reg/1", a.URI)
138
if a.Authz != "https://ca.tld/acme/new-authz" {
139
t.Errorf("a.Authz = %q; want https://ca.tld/acme/new-authz", a.Authz)
141
if a.CurrentTerms != "https://ca.tld/acme/terms" {
142
t.Errorf("a.CurrentTerms = %q; want https://ca.tld/acme/terms", a.CurrentTerms)
144
if !reflect.DeepEqual(a.Contact, contacts) {
145
t.Errorf("a.Contact = %v; want %v", a.Contact, contacts)
149
func TestUpdateReg(t *testing.T) {
150
const terms = "https://ca.tld/acme/terms"
151
contacts := []string{"mailto:admin@example.com"}
153
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
154
if r.Method == "HEAD" {
155
w.Header().Set("replay-nonce", "test-nonce")
158
if r.Method != "POST" {
159
t.Errorf("r.Method = %q; want POST", r.Method)
167
decodeJWSRequest(t, &j, r)
170
if j.Resource != "reg" {
171
t.Errorf("j.Resource = %q; want reg", j.Resource)
173
if j.Agreement != terms {
174
t.Errorf("j.Agreement = %q; want %q", j.Agreement, terms)
176
if !reflect.DeepEqual(j.Contact, contacts) {
177
t.Errorf("j.Contact = %v; want %v", j.Contact, contacts)
180
w.Header().Set("Link", `<https://ca.tld/acme/new-authz>;rel="next"`)
181
w.Header().Add("Link", `<https://ca.tld/acme/recover-reg>;rel="recover"`)
182
w.Header().Add("Link", fmt.Sprintf(`<%s>;rel="terms-of-service"`, terms))
183
w.WriteHeader(http.StatusOK)
184
b, _ := json.Marshal(contacts)
185
fmt.Fprintf(w, `{"contact":%s, "agreement":%q}`, b, terms)
189
c := Client{Key: testKeyEC}
190
a := &Account{URI: ts.URL, Contact: contacts, AgreedTerms: terms}
192
if a, err = c.UpdateReg(context.Background(), a); err != nil {
195
if a.Authz != "https://ca.tld/acme/new-authz" {
196
t.Errorf("a.Authz = %q; want https://ca.tld/acme/new-authz", a.Authz)
198
if a.AgreedTerms != terms {
199
t.Errorf("a.AgreedTerms = %q; want %q", a.AgreedTerms, terms)
201
if a.CurrentTerms != terms {
202
t.Errorf("a.CurrentTerms = %q; want %q", a.CurrentTerms, terms)
205
t.Errorf("a.URI = %q; want %q", a.URI, ts.URL)
209
func TestGetReg(t *testing.T) {
210
const terms = "https://ca.tld/acme/terms"
211
const newTerms = "https://ca.tld/acme/new-terms"
212
contacts := []string{"mailto:admin@example.com"}
214
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
215
if r.Method == "HEAD" {
216
w.Header().Set("replay-nonce", "test-nonce")
219
if r.Method != "POST" {
220
t.Errorf("r.Method = %q; want POST", r.Method)
228
decodeJWSRequest(t, &j, r)
231
if j.Resource != "reg" {
232
t.Errorf("j.Resource = %q; want reg", j.Resource)
234
if len(j.Contact) != 0 {
235
t.Errorf("j.Contact = %v", j.Contact)
237
if j.Agreement != "" {
238
t.Errorf("j.Agreement = %q", j.Agreement)
241
w.Header().Set("Link", `<https://ca.tld/acme/new-authz>;rel="next"`)
242
w.Header().Add("Link", `<https://ca.tld/acme/recover-reg>;rel="recover"`)
243
w.Header().Add("Link", fmt.Sprintf(`<%s>;rel="terms-of-service"`, newTerms))
244
w.WriteHeader(http.StatusOK)
245
b, _ := json.Marshal(contacts)
246
fmt.Fprintf(w, `{"contact":%s, "agreement":%q}`, b, terms)
250
c := Client{Key: testKeyEC}
251
a, err := c.GetReg(context.Background(), ts.URL)
255
if a.Authz != "https://ca.tld/acme/new-authz" {
256
t.Errorf("a.AuthzURL = %q; want https://ca.tld/acme/new-authz", a.Authz)
258
if a.AgreedTerms != terms {
259
t.Errorf("a.AgreedTerms = %q; want %q", a.AgreedTerms, terms)
261
if a.CurrentTerms != newTerms {
262
t.Errorf("a.CurrentTerms = %q; want %q", a.CurrentTerms, newTerms)
265
t.Errorf("a.URI = %q; want %q", a.URI, ts.URL)
269
func TestAuthorize(t *testing.T) {
270
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
271
if r.Method == "HEAD" {
272
w.Header().Set("replay-nonce", "test-nonce")
275
if r.Method != "POST" {
276
t.Errorf("r.Method = %q; want POST", r.Method)
286
decodeJWSRequest(t, &j, r)
289
if j.Resource != "new-authz" {
290
t.Errorf("j.Resource = %q; want new-authz", j.Resource)
292
if j.Identifier.Type != "dns" {
293
t.Errorf("j.Identifier.Type = %q; want dns", j.Identifier.Type)
295
if j.Identifier.Value != "example.com" {
296
t.Errorf("j.Identifier.Value = %q; want example.com", j.Identifier.Value)
299
w.Header().Set("Location", "https://ca.tld/acme/auth/1")
300
w.WriteHeader(http.StatusCreated)
302
"identifier": {"type":"dns","value":"example.com"},
308
"uri":"https://ca.tld/acme/challenge/publickey/id1",
314
"uri":"https://ca.tld/acme/challenge/publickey/id2",
318
"combinations":[[0],[1]]}`)
322
cl := Client{Key: testKeyEC, dir: &Directory{AuthzURL: ts.URL}}
323
auth, err := cl.Authorize(context.Background(), "example.com")
328
if auth.URI != "https://ca.tld/acme/auth/1" {
329
t.Errorf("URI = %q; want https://ca.tld/acme/auth/1", auth.URI)
331
if auth.Status != "pending" {
332
t.Errorf("Status = %q; want pending", auth.Status)
334
if auth.Identifier.Type != "dns" {
335
t.Errorf("Identifier.Type = %q; want dns", auth.Identifier.Type)
337
if auth.Identifier.Value != "example.com" {
338
t.Errorf("Identifier.Value = %q; want example.com", auth.Identifier.Value)
341
if n := len(auth.Challenges); n != 2 {
342
t.Fatalf("len(auth.Challenges) = %d; want 2", n)
345
c := auth.Challenges[0]
346
if c.Type != "http-01" {
347
t.Errorf("c.Type = %q; want http-01", c.Type)
349
if c.URI != "https://ca.tld/acme/challenge/publickey/id1" {
350
t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", c.URI)
352
if c.Token != "token1" {
353
t.Errorf("c.Token = %q; want token1", c.Type)
356
c = auth.Challenges[1]
357
if c.Type != "tls-sni-01" {
358
t.Errorf("c.Type = %q; want tls-sni-01", c.Type)
360
if c.URI != "https://ca.tld/acme/challenge/publickey/id2" {
361
t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id2", c.URI)
363
if c.Token != "token2" {
364
t.Errorf("c.Token = %q; want token2", c.Type)
367
combs := [][]int{{0}, {1}}
368
if !reflect.DeepEqual(auth.Combinations, combs) {
369
t.Errorf("auth.Combinations: %+v\nwant: %+v\n", auth.Combinations, combs)
373
func TestAuthorizeValid(t *testing.T) {
374
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
375
if r.Method == "HEAD" {
376
w.Header().Set("replay-nonce", "nonce")
379
w.WriteHeader(http.StatusCreated)
380
w.Write([]byte(`{"status":"valid"}`))
383
client := Client{Key: testKey, dir: &Directory{AuthzURL: ts.URL}}
384
_, err := client.Authorize(context.Background(), "example.com")
386
t.Errorf("err = %v", err)
390
func TestGetAuthorization(t *testing.T) {
391
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
392
if r.Method != "GET" {
393
t.Errorf("r.Method = %q; want GET", r.Method)
396
w.WriteHeader(http.StatusOK)
398
"identifier": {"type":"dns","value":"example.com"},
404
"uri":"https://ca.tld/acme/challenge/publickey/id1",
410
"uri":"https://ca.tld/acme/challenge/publickey/id2",
414
"combinations":[[0],[1]]}`)
418
cl := Client{Key: testKeyEC}
419
auth, err := cl.GetAuthorization(context.Background(), ts.URL)
424
if auth.Status != "pending" {
425
t.Errorf("Status = %q; want pending", auth.Status)
427
if auth.Identifier.Type != "dns" {
428
t.Errorf("Identifier.Type = %q; want dns", auth.Identifier.Type)
430
if auth.Identifier.Value != "example.com" {
431
t.Errorf("Identifier.Value = %q; want example.com", auth.Identifier.Value)
434
if n := len(auth.Challenges); n != 2 {
435
t.Fatalf("len(set.Challenges) = %d; want 2", n)
438
c := auth.Challenges[0]
439
if c.Type != "http-01" {
440
t.Errorf("c.Type = %q; want http-01", c.Type)
442
if c.URI != "https://ca.tld/acme/challenge/publickey/id1" {
443
t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", c.URI)
445
if c.Token != "token1" {
446
t.Errorf("c.Token = %q; want token1", c.Type)
449
c = auth.Challenges[1]
450
if c.Type != "tls-sni-01" {
451
t.Errorf("c.Type = %q; want tls-sni-01", c.Type)
453
if c.URI != "https://ca.tld/acme/challenge/publickey/id2" {
454
t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id2", c.URI)
456
if c.Token != "token2" {
457
t.Errorf("c.Token = %q; want token2", c.Type)
460
combs := [][]int{{0}, {1}}
461
if !reflect.DeepEqual(auth.Combinations, combs) {
462
t.Errorf("auth.Combinations: %+v\nwant: %+v\n", auth.Combinations, combs)
466
func TestWaitAuthorization(t *testing.T) {
468
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
470
w.Header().Set("retry-after", "0")
472
fmt.Fprintf(w, `{"status":"valid"}`)
475
fmt.Fprintf(w, `{"status":"pending"}`)
483
done := make(chan res)
487
a, err := client.WaitAuthorization(context.Background(), ts.URL)
492
case <-time.After(5 * time.Second):
493
t.Fatal("WaitAuthz took too long to return")
496
t.Fatalf("res.err = %v", res.err)
498
if res.authz == nil {
499
t.Fatal("res.authz is nil")
504
func TestWaitAuthorizationInvalid(t *testing.T) {
505
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
506
fmt.Fprintf(w, `{"status":"invalid"}`)
510
res := make(chan error)
514
_, err := client.WaitAuthorization(context.Background(), ts.URL)
519
case <-time.After(3 * time.Second):
520
t.Fatal("WaitAuthz took too long to return")
523
t.Error("err is nil")
528
func TestWaitAuthorizationCancel(t *testing.T) {
529
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
530
w.Header().Set("retry-after", "60")
531
fmt.Fprintf(w, `{"status":"pending"}`)
535
res := make(chan error)
539
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
541
_, err := client.WaitAuthorization(ctx, ts.URL)
546
case <-time.After(time.Second):
547
t.Fatal("WaitAuthz took too long to return")
550
t.Error("err is nil")
555
func TestRevokeAuthorization(t *testing.T) {
556
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
557
if r.Method == "HEAD" {
558
w.Header().Set("replay-nonce", "nonce")
567
decodeJWSRequest(t, &req, r)
568
if req.Resource != "authz" {
569
t.Errorf("req.Resource = %q; want authz", req.Resource)
572
t.Errorf("req.Delete is false")
575
w.WriteHeader(http.StatusInternalServerError)
579
client := &Client{Key: testKey}
580
ctx := context.Background()
581
if err := client.RevokeAuthorization(ctx, ts.URL+"/1"); err != nil {
582
t.Errorf("err = %v", err)
584
if client.RevokeAuthorization(ctx, ts.URL+"/2") == nil {
589
func TestPollChallenge(t *testing.T) {
590
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
591
if r.Method != "GET" {
592
t.Errorf("r.Method = %q; want GET", r.Method)
595
w.WriteHeader(http.StatusOK)
599
"uri":"https://ca.tld/acme/challenge/publickey/id1",
604
cl := Client{Key: testKeyEC}
605
chall, err := cl.GetChallenge(context.Background(), ts.URL)
610
if chall.Status != "pending" {
611
t.Errorf("Status = %q; want pending", chall.Status)
613
if chall.Type != "http-01" {
614
t.Errorf("c.Type = %q; want http-01", chall.Type)
616
if chall.URI != "https://ca.tld/acme/challenge/publickey/id1" {
617
t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", chall.URI)
619
if chall.Token != "token1" {
620
t.Errorf("c.Token = %q; want token1", chall.Type)
624
func TestAcceptChallenge(t *testing.T) {
625
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
626
if r.Method == "HEAD" {
627
w.Header().Set("replay-nonce", "test-nonce")
630
if r.Method != "POST" {
631
t.Errorf("r.Method = %q; want POST", r.Method)
637
Auth string `json:"keyAuthorization"`
639
decodeJWSRequest(t, &j, r)
642
if j.Resource != "challenge" {
643
t.Errorf(`resource = %q; want "challenge"`, j.Resource)
645
if j.Type != "http-01" {
646
t.Errorf(`type = %q; want "http-01"`, j.Type)
648
keyAuth := "token1." + testKeyECThumbprint
649
if j.Auth != keyAuth {
650
t.Errorf(`keyAuthorization = %q; want %q`, j.Auth, keyAuth)
653
// Respond to request
654
w.WriteHeader(http.StatusAccepted)
658
"uri":"https://ca.tld/acme/challenge/publickey/id1",
660
"keyAuthorization":%q
665
cl := Client{Key: testKeyEC}
666
c, err := cl.Accept(context.Background(), &Challenge{
675
if c.Type != "http-01" {
676
t.Errorf("c.Type = %q; want http-01", c.Type)
678
if c.URI != "https://ca.tld/acme/challenge/publickey/id1" {
679
t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", c.URI)
681
if c.Token != "token1" {
682
t.Errorf("c.Token = %q; want token1", c.Type)
686
func TestNewCert(t *testing.T) {
687
notBefore := time.Now()
688
notAfter := notBefore.AddDate(0, 2, 0)
689
timeNow = func() time.Time { return notBefore }
691
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
692
if r.Method == "HEAD" {
693
w.Header().Set("replay-nonce", "test-nonce")
696
if r.Method != "POST" {
697
t.Errorf("r.Method = %q; want POST", r.Method)
701
Resource string `json:"resource"`
702
CSR string `json:"csr"`
703
NotBefore string `json:"notBefore,omitempty"`
704
NotAfter string `json:"notAfter,omitempty"`
706
decodeJWSRequest(t, &j, r)
709
if j.Resource != "new-cert" {
710
t.Errorf(`resource = %q; want "new-cert"`, j.Resource)
712
if j.NotBefore != notBefore.Format(time.RFC3339) {
713
t.Errorf(`notBefore = %q; wanted %q`, j.NotBefore, notBefore.Format(time.RFC3339))
715
if j.NotAfter != notAfter.Format(time.RFC3339) {
716
t.Errorf(`notAfter = %q; wanted %q`, j.NotAfter, notAfter.Format(time.RFC3339))
719
// Respond to request
720
template := x509.Certificate{
721
SerialNumber: big.NewInt(int64(1)),
723
Organization: []string{"goacme"},
725
NotBefore: notBefore,
728
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
729
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
730
BasicConstraintsValid: true,
733
sampleCert, err := x509.CreateCertificate(rand.Reader, &template, &template, &testKeyEC.PublicKey, testKeyEC)
735
t.Fatalf("Error creating certificate: %v", err)
738
w.Header().Set("Location", "https://ca.tld/acme/cert/1")
739
w.WriteHeader(http.StatusCreated)
744
csr := x509.CertificateRequest{
747
CommonName: "example.com",
748
Organization: []string{"goacme"},
751
csrb, err := x509.CreateCertificateRequest(rand.Reader, &csr, testKeyEC)
756
c := Client{Key: testKeyEC, dir: &Directory{CertURL: ts.URL}}
757
cert, certURL, err := c.CreateCert(context.Background(), csrb, notAfter.Sub(notBefore), false)
762
t.Errorf("cert is nil")
764
if certURL != "https://ca.tld/acme/cert/1" {
765
t.Errorf("certURL = %q; want https://ca.tld/acme/cert/1", certURL)
769
func TestFetchCert(t *testing.T) {
771
var ts *httptest.Server
772
ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
775
up := fmt.Sprintf("<%s>;rel=up", ts.URL)
776
w.Header().Set("link", up)
778
w.Write([]byte{count})
781
res, err := (&Client{}).FetchCert(context.Background(), ts.URL, true)
783
t.Fatalf("FetchCert: %v", err)
785
cert := [][]byte{{1}, {2}, {3}}
786
if !reflect.DeepEqual(res, cert) {
787
t.Errorf("res = %v; want %v", res, cert)
791
func TestFetchCertRetry(t *testing.T) {
793
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
795
w.Header().Set("retry-after", "0")
796
w.WriteHeader(http.StatusAccepted)
803
res, err := (&Client{}).FetchCert(context.Background(), ts.URL, false)
805
t.Fatalf("FetchCert: %v", err)
807
cert := [][]byte{{1}}
808
if !reflect.DeepEqual(res, cert) {
809
t.Errorf("res = %v; want %v", res, cert)
813
func TestFetchCertCancel(t *testing.T) {
814
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
815
w.Header().Set("retry-after", "0")
816
w.WriteHeader(http.StatusAccepted)
819
ctx, cancel := context.WithCancel(context.Background())
820
done := make(chan struct{})
823
_, err = (&Client{}).FetchCert(ctx, ts.URL, false)
828
if err != context.Canceled {
829
t.Errorf("err = %v; want %v", err, context.Canceled)
833
func TestFetchCertDepth(t *testing.T) {
835
var ts *httptest.Server
836
ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
838
if count > maxChainLen+1 {
839
t.Errorf("count = %d; want at most %d", count, maxChainLen+1)
840
w.WriteHeader(http.StatusInternalServerError)
842
w.Header().Set("link", fmt.Sprintf("<%s>;rel=up", ts.URL))
843
w.Write([]byte{count})
846
_, err := (&Client{}).FetchCert(context.Background(), ts.URL, true)
848
t.Errorf("err is nil")
852
func TestFetchCertBreadth(t *testing.T) {
853
var ts *httptest.Server
854
ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
855
for i := 0; i < maxChainLen+1; i++ {
856
w.Header().Add("link", fmt.Sprintf("<%s>;rel=up", ts.URL))
861
_, err := (&Client{}).FetchCert(context.Background(), ts.URL, true)
863
t.Errorf("err is nil")
867
func TestFetchCertSize(t *testing.T) {
868
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
869
b := bytes.Repeat([]byte{1}, maxCertSize+1)
873
_, err := (&Client{}).FetchCert(context.Background(), ts.URL, false)
875
t.Errorf("err is nil")
879
func TestRevokeCert(t *testing.T) {
880
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
881
if r.Method == "HEAD" {
882
w.Header().Set("replay-nonce", "nonce")
891
decodeJWSRequest(t, &req, r)
892
if req.Resource != "revoke-cert" {
893
t.Errorf("req.Resource = %q; want revoke-cert", req.Resource)
896
t.Errorf("req.Reason = %d; want 1", req.Reason)
898
// echo -n cert | base64 | tr -d '=' | tr '/+' '_-'
900
if req.Certificate != cert {
901
t.Errorf("req.Certificate = %q; want %q", req.Certificate, cert)
907
dir: &Directory{RevokeURL: ts.URL},
909
ctx := context.Background()
910
if err := client.RevokeCert(ctx, nil, []byte("cert"), CRLReasonKeyCompromise); err != nil {
915
func TestFetchNonce(t *testing.T) {
920
{http.StatusOK, "nonce1"},
921
{http.StatusBadRequest, "nonce2"},
925
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
926
if r.Method != "HEAD" {
927
t.Errorf("%d: r.Method = %q; want HEAD", i, r.Method)
929
w.Header().Set("replay-nonce", tests[i].nonce)
930
w.WriteHeader(tests[i].code)
933
for ; i < len(tests); i++ {
935
n, err := fetchNonce(context.Background(), http.DefaultClient, ts.URL)
937
t.Errorf("%d: n=%q; want %q", i, n, test.nonce)
940
case err == nil && test.nonce == "":
941
t.Errorf("%d: n=%q, err=%v; want non-nil error", i, n, err)
942
case err != nil && test.nonce != "":
943
t.Errorf("%d: n=%q, err=%v; want %q", i, n, err, test.nonce)
948
func TestLinkHeader(t *testing.T) {
949
h := http.Header{"Link": {
950
`<https://example.com/acme/new-authz>;rel="next"`,
951
`<https://example.com/acme/recover-reg>; rel=recover`,
952
`<https://example.com/acme/terms>; foo=bar; rel="terms-of-service"`,
959
{"next", []string{"https://example.com/acme/new-authz", "dup"}},
960
{"recover", []string{"https://example.com/acme/recover-reg"}},
961
{"terms-of-service", []string{"https://example.com/acme/terms"}},
964
for i, test := range tests {
965
if v := linkHeader(h, test.rel); !reflect.DeepEqual(v, test.out) {
966
t.Errorf("%d: linkHeader(%q): %v; want %v", i, test.rel, v, test.out)
971
func TestErrorResponse(t *testing.T) {
974
"type": "urn:acme:error:xxx",
977
res := &http.Response{
979
Status: "400 Bad Request",
980
Body: ioutil.NopCloser(strings.NewReader(s)),
981
Header: http.Header{"X-Foo": {"bar"}},
983
err := responseError(res)
984
v, ok := err.(*Error)
986
t.Fatalf("err = %+v (%T); want *Error type", err, err)
988
if v.StatusCode != 400 {
989
t.Errorf("v.StatusCode = %v; want 400", v.StatusCode)
991
if v.ProblemType != "urn:acme:error:xxx" {
992
t.Errorf("v.ProblemType = %q; want urn:acme:error:xxx", v.ProblemType)
994
if v.Detail != "text" {
995
t.Errorf("v.Detail = %q; want text", v.Detail)
997
if !reflect.DeepEqual(v.Header, res.Header) {
998
t.Errorf("v.Header = %+v; want %+v", v.Header, res.Header)
1002
func TestTLSSNI01ChallengeCert(t *testing.T) {
1004
token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
1005
// echo -n <token.testKeyECThumbprint> | shasum -a 256
1006
san = "dbbd5eefe7b4d06eb9d1d9f5acb4c7cd.a27d320e4b30332f0b6cb441734ad7b0.acme.invalid"
1009
client := &Client{Key: testKeyEC}
1010
tlscert, name, err := client.TLSSNI01ChallengeCert(token)
1015
if n := len(tlscert.Certificate); n != 1 {
1016
t.Fatalf("len(tlscert.Certificate) = %d; want 1", n)
1018
cert, err := x509.ParseCertificate(tlscert.Certificate[0])
1022
if len(cert.DNSNames) != 1 || cert.DNSNames[0] != san {
1023
t.Fatalf("cert.DNSNames = %v; want %q", cert.DNSNames, san)
1025
if cert.DNSNames[0] != name {
1026
t.Errorf("cert.DNSNames[0] != name: %q vs %q", cert.DNSNames[0], name)
1030
func TestTLSSNI02ChallengeCert(t *testing.T) {
1032
token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
1033
// echo -n evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA | shasum -a 256
1034
sanA = "7ea0aaa69214e71e02cebb18bb867736.09b730209baabf60e43d4999979ff139.token.acme.invalid"
1035
// echo -n <token.testKeyECThumbprint> | shasum -a 256
1036
sanB = "dbbd5eefe7b4d06eb9d1d9f5acb4c7cd.a27d320e4b30332f0b6cb441734ad7b0.ka.acme.invalid"
1039
client := &Client{Key: testKeyEC}
1040
tlscert, name, err := client.TLSSNI02ChallengeCert(token)
1045
if n := len(tlscert.Certificate); n != 1 {
1046
t.Fatalf("len(tlscert.Certificate) = %d; want 1", n)
1048
cert, err := x509.ParseCertificate(tlscert.Certificate[0])
1052
names := []string{sanA, sanB}
1053
if !reflect.DeepEqual(cert.DNSNames, names) {
1054
t.Fatalf("cert.DNSNames = %v;\nwant %v", cert.DNSNames, names)
1056
sort.Strings(cert.DNSNames)
1057
i := sort.SearchStrings(cert.DNSNames, name)
1058
if i >= len(cert.DNSNames) || cert.DNSNames[i] != name {
1059
t.Errorf("%v doesn't have %q", cert.DNSNames, name)
1063
func TestTLSChallengeCertOpt(t *testing.T) {
1064
key, err := rsa.GenerateKey(rand.Reader, 512)
1068
tmpl := &x509.Certificate{
1069
SerialNumber: big.NewInt(2),
1070
Subject: pkix.Name{Organization: []string{"Test"}},
1071
DNSNames: []string{"should-be-overwritten"},
1073
opts := []CertOption{WithKey(key), WithTemplate(tmpl)}
1075
client := &Client{Key: testKeyEC}
1076
cert1, _, err := client.TLSSNI01ChallengeCert("token", opts...)
1080
cert2, _, err := client.TLSSNI02ChallengeCert("token", opts...)
1085
for i, tlscert := range []tls.Certificate{cert1, cert2} {
1086
// verify generated cert private key
1087
tlskey, ok := tlscert.PrivateKey.(*rsa.PrivateKey)
1089
t.Errorf("%d: tlscert.PrivateKey is %T; want *rsa.PrivateKey", i, tlscert.PrivateKey)
1092
if tlskey.D.Cmp(key.D) != 0 {
1093
t.Errorf("%d: tlskey.D = %v; want %v", i, tlskey.D, key.D)
1095
// verify generated cert public key
1096
x509Cert, err := x509.ParseCertificate(tlscert.Certificate[0])
1098
t.Errorf("%d: %v", i, err)
1101
tlspub, ok := x509Cert.PublicKey.(*rsa.PublicKey)
1103
t.Errorf("%d: x509Cert.PublicKey is %T; want *rsa.PublicKey", i, x509Cert.PublicKey)
1106
if tlspub.N.Cmp(key.N) != 0 {
1107
t.Errorf("%d: tlspub.N = %v; want %v", i, tlspub.N, key.N)
1109
// verify template option
1111
if x509Cert.SerialNumber.Cmp(sn) != 0 {
1112
t.Errorf("%d: SerialNumber = %v; want %v", i, x509Cert.SerialNumber, sn)
1114
org := []string{"Test"}
1115
if !reflect.DeepEqual(x509Cert.Subject.Organization, org) {
1116
t.Errorf("%d: Subject.Organization = %+v; want %+v", i, x509Cert.Subject.Organization, org)
1118
for _, v := range x509Cert.DNSNames {
1119
if !strings.HasSuffix(v, ".acme.invalid") {
1120
t.Errorf("%d: invalid DNSNames element: %q", i, v)
1126
func TestHTTP01Challenge(t *testing.T) {
1129
// thumbprint is precomputed for testKeyEC in jws_test.go
1130
value = token + "." + testKeyECThumbprint
1131
urlpath = "/.well-known/acme-challenge/" + token
1133
client := &Client{Key: testKeyEC}
1134
val, err := client.HTTP01ChallengeResponse(token)
1139
t.Errorf("val = %q; want %q", val, value)
1141
if path := client.HTTP01ChallengePath(token); path != urlpath {
1142
t.Errorf("path = %q; want %q", path, urlpath)
1146
func TestDNS01ChallengeRecord(t *testing.T) {
1147
// echo -n xxx.<testKeyECThumbprint> | \
1148
// openssl dgst -binary -sha256 | \
1149
// base64 | tr -d '=' | tr '/+' '_-'
1150
const value = "8DERMexQ5VcdJ_prpPiA0mVdp7imgbCgjsG4SqqNMIo"
1152
client := &Client{Key: testKeyEC}
1153
val, err := client.DNS01ChallengeRecord("xxx")
1158
t.Errorf("val = %q; want %q", val, value)
1162
func TestBackoff(t *testing.T) {
1163
tt := []struct{ min, max time.Duration }{
1164
{time.Second, 2 * time.Second},
1165
{2 * time.Second, 3 * time.Second},
1166
{4 * time.Second, 5 * time.Second},
1167
{8 * time.Second, 9 * time.Second},
1169
for i, test := range tt {
1170
d := backoff(i, time.Minute)
1171
if d < test.min || test.max < d {
1172
t.Errorf("%d: d = %v; want between %v and %v", i, d, test.min, test.max)
1176
min, max := time.Second, 2*time.Second
1177
if d := backoff(-1, time.Minute); d < min || max < d {
1178
t.Errorf("d = %v; want between %v and %v", d, min, max)
1181
bound := 10 * time.Second
1182
if d := backoff(100, bound); d != bound {
1183
t.Errorf("d = %v; want %v", d, bound)