acme_test.go 32 KB

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