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