acme_test.go 29 KB

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