1// Copyright 2019 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
5package acme
6
7import (
8	"bytes"
9	"context"
10	"crypto/rand"
11	"crypto/x509"
12	"crypto/x509/pkix"
13	"encoding/json"
14	"encoding/pem"
15	"fmt"
16	"io/ioutil"
17	"math/big"
18	"net/http"
19	"net/http/httptest"
20	"reflect"
21	"sync"
22	"testing"
23	"time"
24)
25
26// While contents of this file is pertinent only to RFC8555,
27// it is complementary to the tests in the other _test.go files
28// many of which are valid for both pre- and RFC8555.
29// This will make it easier to clean up the tests once non-RFC compliant
30// code is removed.
31
32func TestRFC_Discover(t *testing.T) {
33	const (
34		nonce       = "https://example.com/acme/new-nonce"
35		reg         = "https://example.com/acme/new-acct"
36		order       = "https://example.com/acme/new-order"
37		authz       = "https://example.com/acme/new-authz"
38		revoke      = "https://example.com/acme/revoke-cert"
39		keychange   = "https://example.com/acme/key-change"
40		metaTerms   = "https://example.com/acme/terms/2017-5-30"
41		metaWebsite = "https://www.example.com/"
42		metaCAA     = "example.com"
43	)
44	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
45		w.Header().Set("Content-Type", "application/json")
46		fmt.Fprintf(w, `{
47			"newNonce": %q,
48			"newAccount": %q,
49			"newOrder": %q,
50			"newAuthz": %q,
51			"revokeCert": %q,
52			"keyChange": %q,
53			"meta": {
54				"termsOfService": %q,
55				"website": %q,
56				"caaIdentities": [%q],
57				"externalAccountRequired": true
58			}
59		}`, nonce, reg, order, authz, revoke, keychange, metaTerms, metaWebsite, metaCAA)
60	}))
61	defer ts.Close()
62	c := Client{DirectoryURL: ts.URL}
63	dir, err := c.Discover(context.Background())
64	if err != nil {
65		t.Fatal(err)
66	}
67	if dir.NonceURL != nonce {
68		t.Errorf("dir.NonceURL = %q; want %q", dir.NonceURL, nonce)
69	}
70	if dir.RegURL != reg {
71		t.Errorf("dir.RegURL = %q; want %q", dir.RegURL, reg)
72	}
73	if dir.OrderURL != order {
74		t.Errorf("dir.OrderURL = %q; want %q", dir.OrderURL, order)
75	}
76	if dir.AuthzURL != authz {
77		t.Errorf("dir.AuthzURL = %q; want %q", dir.AuthzURL, authz)
78	}
79	if dir.RevokeURL != revoke {
80		t.Errorf("dir.RevokeURL = %q; want %q", dir.RevokeURL, revoke)
81	}
82	if dir.KeyChangeURL != keychange {
83		t.Errorf("dir.KeyChangeURL = %q; want %q", dir.KeyChangeURL, keychange)
84	}
85	if dir.Terms != metaTerms {
86		t.Errorf("dir.Terms = %q; want %q", dir.Terms, metaTerms)
87	}
88	if dir.Website != metaWebsite {
89		t.Errorf("dir.Website = %q; want %q", dir.Website, metaWebsite)
90	}
91	if len(dir.CAA) == 0 || dir.CAA[0] != metaCAA {
92		t.Errorf("dir.CAA = %q; want [%q]", dir.CAA, metaCAA)
93	}
94	if !dir.ExternalAccountRequired {
95		t.Error("dir.Meta.ExternalAccountRequired is false")
96	}
97}
98
99func TestRFC_popNonce(t *testing.T) {
100	var count int
101	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
102		// The Client uses only Directory.NonceURL when specified.
103		// Expect no other URL paths.
104		if r.URL.Path != "/new-nonce" {
105			t.Errorf("r.URL.Path = %q; want /new-nonce", r.URL.Path)
106		}
107		if count > 0 {
108			w.WriteHeader(http.StatusTooManyRequests)
109			return
110		}
111		count++
112		w.Header().Set("Replay-Nonce", "second")
113	}))
114	cl := &Client{
115		DirectoryURL: ts.URL,
116		dir:          &Directory{NonceURL: ts.URL + "/new-nonce"},
117	}
118	cl.addNonce(http.Header{"Replay-Nonce": {"first"}})
119
120	for i, nonce := range []string{"first", "second"} {
121		v, err := cl.popNonce(context.Background(), "")
122		if err != nil {
123			t.Errorf("%d: cl.popNonce: %v", i, err)
124		}
125		if v != nonce {
126			t.Errorf("%d: cl.popNonce = %q; want %q", i, v, nonce)
127		}
128	}
129	// No more nonces and server replies with an error past first nonce fetch.
130	// Expected to fail.
131	if _, err := cl.popNonce(context.Background(), ""); err == nil {
132		t.Error("last cl.popNonce returned nil error")
133	}
134}
135
136func TestRFC_postKID(t *testing.T) {
137	var ts *httptest.Server
138	ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
139		switch r.URL.Path {
140		case "/new-nonce":
141			w.Header().Set("Replay-Nonce", "nonce")
142		case "/new-account":
143			w.Header().Set("Location", "/account-1")
144			w.Write([]byte(`{"status":"valid"}`))
145		case "/post":
146			b, _ := ioutil.ReadAll(r.Body) // check err later in decodeJWSxxx
147			head, err := decodeJWSHead(bytes.NewReader(b))
148			if err != nil {
149				t.Errorf("decodeJWSHead: %v", err)
150				return
151			}
152			if head.KID != "/account-1" {
153				t.Errorf("head.KID = %q; want /account-1", head.KID)
154			}
155			if len(head.JWK) != 0 {
156				t.Errorf("head.JWK = %q; want zero map", head.JWK)
157			}
158			if v := ts.URL + "/post"; head.URL != v {
159				t.Errorf("head.URL = %q; want %q", head.URL, v)
160			}
161
162			var payload struct{ Msg string }
163			decodeJWSRequest(t, &payload, bytes.NewReader(b))
164			if payload.Msg != "ping" {
165				t.Errorf("payload.Msg = %q; want ping", payload.Msg)
166			}
167			w.Write([]byte("pong"))
168		default:
169			t.Errorf("unhandled %s %s", r.Method, r.URL)
170			w.WriteHeader(http.StatusBadRequest)
171		}
172	}))
173	defer ts.Close()
174
175	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
176	defer cancel()
177	cl := &Client{
178		Key:          testKey,
179		DirectoryURL: ts.URL,
180		dir: &Directory{
181			NonceURL: ts.URL + "/new-nonce",
182			RegURL:   ts.URL + "/new-account",
183			OrderURL: "/force-rfc-mode",
184		},
185	}
186	req := json.RawMessage(`{"msg":"ping"}`)
187	res, err := cl.post(ctx, nil /* use kid */, ts.URL+"/post", req, wantStatus(http.StatusOK))
188	if err != nil {
189		t.Fatal(err)
190	}
191	defer res.Body.Close()
192	b, _ := ioutil.ReadAll(res.Body) // don't care about err - just checking b
193	if string(b) != "pong" {
194		t.Errorf("res.Body = %q; want pong", b)
195	}
196}
197
198// acmeServer simulates a subset of RFC8555 compliant CA.
199//
200// TODO: We also have x/crypto/acme/autocert/acmetest and startACMEServerStub in autocert_test.go.
201// It feels like this acmeServer is a sweet spot between usefulness and added complexity.
202// Also, acmetest and startACMEServerStub were both written for draft-02, no RFC support.
203// The goal is to consolidate all into one ACME test server.
204type acmeServer struct {
205	ts      *httptest.Server
206	handler map[string]http.HandlerFunc // keyed by r.URL.Path
207
208	mu     sync.Mutex
209	nnonce int
210}
211
212func newACMEServer() *acmeServer {
213	return &acmeServer{handler: make(map[string]http.HandlerFunc)}
214}
215
216func (s *acmeServer) handle(path string, f func(http.ResponseWriter, *http.Request)) {
217	s.handler[path] = http.HandlerFunc(f)
218}
219
220func (s *acmeServer) start() {
221	s.ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
222		w.Header().Set("Content-Type", "application/json")
223
224		// Directory request.
225		if r.URL.Path == "/" {
226			fmt.Fprintf(w, `{
227				"newNonce": %q,
228				"newAccount": %q,
229				"newOrder": %q,
230				"newAuthz": %q,
231				"revokeCert": %q,
232				"meta": {"termsOfService": %q}
233				}`,
234				s.url("/acme/new-nonce"),
235				s.url("/acme/new-account"),
236				s.url("/acme/new-order"),
237				s.url("/acme/new-authz"),
238				s.url("/acme/revoke-cert"),
239				s.url("/terms"),
240			)
241			return
242		}
243
244		// All other responses contain a nonce value unconditionally.
245		w.Header().Set("Replay-Nonce", s.nonce())
246		if r.URL.Path == "/acme/new-nonce" {
247			return
248		}
249
250		h := s.handler[r.URL.Path]
251		if h == nil {
252			w.WriteHeader(http.StatusBadRequest)
253			fmt.Fprintf(w, "Unhandled %s", r.URL.Path)
254			return
255		}
256		h.ServeHTTP(w, r)
257	}))
258}
259
260func (s *acmeServer) close() {
261	s.ts.Close()
262}
263
264func (s *acmeServer) url(path string) string {
265	return s.ts.URL + path
266}
267
268func (s *acmeServer) nonce() string {
269	s.mu.Lock()
270	defer s.mu.Unlock()
271	s.nnonce++
272	return fmt.Sprintf("nonce%d", s.nnonce)
273}
274
275func (s *acmeServer) error(w http.ResponseWriter, e *wireError) {
276	w.WriteHeader(e.Status)
277	json.NewEncoder(w).Encode(e)
278}
279
280func TestRFC_Register(t *testing.T) {
281	const email = "mailto:user@example.org"
282
283	s := newACMEServer()
284	s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
285		w.Header().Set("Location", s.url("/accounts/1"))
286		w.WriteHeader(http.StatusCreated) // 201 means new account created
287		fmt.Fprintf(w, `{
288			"status": "valid",
289			"contact": [%q],
290			"orders": %q
291		}`, email, s.url("/accounts/1/orders"))
292
293		b, _ := ioutil.ReadAll(r.Body) // check err later in decodeJWSxxx
294		head, err := decodeJWSHead(bytes.NewReader(b))
295		if err != nil {
296			t.Errorf("decodeJWSHead: %v", err)
297			return
298		}
299		if len(head.JWK) == 0 {
300			t.Error("head.JWK is empty")
301		}
302
303		var req struct{ Contact []string }
304		decodeJWSRequest(t, &req, bytes.NewReader(b))
305		if len(req.Contact) != 1 || req.Contact[0] != email {
306			t.Errorf("req.Contact = %q; want [%q]", req.Contact, email)
307		}
308	})
309	s.start()
310	defer s.close()
311
312	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
313	defer cancel()
314	cl := &Client{
315		Key:          testKeyEC,
316		DirectoryURL: s.url("/"),
317	}
318
319	var didPrompt bool
320	a := &Account{Contact: []string{email}}
321	acct, err := cl.Register(ctx, a, func(tos string) bool {
322		didPrompt = true
323		terms := s.url("/terms")
324		if tos != terms {
325			t.Errorf("tos = %q; want %q", tos, terms)
326		}
327		return true
328	})
329	if err != nil {
330		t.Fatal(err)
331	}
332	okAccount := &Account{
333		URI:       s.url("/accounts/1"),
334		Status:    StatusValid,
335		Contact:   []string{email},
336		OrdersURL: s.url("/accounts/1/orders"),
337	}
338	if !reflect.DeepEqual(acct, okAccount) {
339		t.Errorf("acct = %+v; want %+v", acct, okAccount)
340	}
341	if !didPrompt {
342		t.Error("tos prompt wasn't called")
343	}
344	if v := cl.accountKID(ctx); v != keyID(okAccount.URI) {
345		t.Errorf("account kid = %q; want %q", v, okAccount.URI)
346	}
347}
348
349func TestRFC_RegisterExisting(t *testing.T) {
350	s := newACMEServer()
351	s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
352		w.Header().Set("Location", s.url("/accounts/1"))
353		w.WriteHeader(http.StatusOK) // 200 means account already exists
354		w.Write([]byte(`{"status": "valid"}`))
355	})
356	s.start()
357	defer s.close()
358
359	cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
360	_, err := cl.Register(context.Background(), &Account{}, AcceptTOS)
361	if err != ErrAccountAlreadyExists {
362		t.Errorf("err = %v; want %v", err, ErrAccountAlreadyExists)
363	}
364	kid := keyID(s.url("/accounts/1"))
365	if v := cl.accountKID(context.Background()); v != kid {
366		t.Errorf("account kid = %q; want %q", v, kid)
367	}
368}
369
370func TestRFC_UpdateReg(t *testing.T) {
371	const email = "mailto:user@example.org"
372
373	s := newACMEServer()
374	s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
375		w.Header().Set("Location", s.url("/accounts/1"))
376		w.WriteHeader(http.StatusOK)
377		w.Write([]byte(`{"status": "valid"}`))
378	})
379	var didUpdate bool
380	s.handle("/accounts/1", func(w http.ResponseWriter, r *http.Request) {
381		didUpdate = true
382		w.Header().Set("Location", s.url("/accounts/1"))
383		w.WriteHeader(http.StatusOK)
384		w.Write([]byte(`{"status": "valid"}`))
385
386		b, _ := ioutil.ReadAll(r.Body) // check err later in decodeJWSxxx
387		head, err := decodeJWSHead(bytes.NewReader(b))
388		if err != nil {
389			t.Errorf("decodeJWSHead: %v", err)
390			return
391		}
392		if len(head.JWK) != 0 {
393			t.Error("head.JWK is non-zero")
394		}
395		kid := s.url("/accounts/1")
396		if head.KID != kid {
397			t.Errorf("head.KID = %q; want %q", head.KID, kid)
398		}
399
400		var req struct{ Contact []string }
401		decodeJWSRequest(t, &req, bytes.NewReader(b))
402		if len(req.Contact) != 1 || req.Contact[0] != email {
403			t.Errorf("req.Contact = %q; want [%q]", req.Contact, email)
404		}
405	})
406	s.start()
407	defer s.close()
408
409	cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
410	_, err := cl.UpdateReg(context.Background(), &Account{Contact: []string{email}})
411	if err != nil {
412		t.Error(err)
413	}
414	if !didUpdate {
415		t.Error("UpdateReg didn't update the account")
416	}
417}
418
419func TestRFC_GetReg(t *testing.T) {
420	s := newACMEServer()
421	s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
422		w.Header().Set("Location", s.url("/accounts/1"))
423		w.WriteHeader(http.StatusOK)
424		w.Write([]byte(`{"status": "valid"}`))
425
426		head, err := decodeJWSHead(r.Body)
427		if err != nil {
428			t.Errorf("decodeJWSHead: %v", err)
429			return
430		}
431		if len(head.JWK) == 0 {
432			t.Error("head.JWK is empty")
433		}
434	})
435	s.start()
436	defer s.close()
437
438	cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
439	acct, err := cl.GetReg(context.Background(), "")
440	if err != nil {
441		t.Fatal(err)
442	}
443	okAccount := &Account{
444		URI:    s.url("/accounts/1"),
445		Status: StatusValid,
446	}
447	if !reflect.DeepEqual(acct, okAccount) {
448		t.Errorf("acct = %+v; want %+v", acct, okAccount)
449	}
450}
451
452func TestRFC_GetRegNoAccount(t *testing.T) {
453	s := newACMEServer()
454	s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
455		s.error(w, &wireError{
456			Status: http.StatusBadRequest,
457			Type:   "urn:ietf:params:acme:error:accountDoesNotExist",
458		})
459	})
460	s.start()
461	defer s.close()
462
463	cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
464	if _, err := cl.GetReg(context.Background(), ""); err != ErrNoAccount {
465		t.Errorf("err = %v; want %v", err, ErrNoAccount)
466	}
467}
468
469func TestRFC_GetRegOtherError(t *testing.T) {
470	s := newACMEServer()
471	s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
472		w.WriteHeader(http.StatusBadRequest)
473	})
474	s.start()
475	defer s.close()
476
477	cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
478	if _, err := cl.GetReg(context.Background(), ""); err == nil || err == ErrNoAccount {
479		t.Errorf("GetReg: %v; want any other non-nil err", err)
480	}
481}
482
483func TestRFC_AuthorizeOrder(t *testing.T) {
484	s := newACMEServer()
485	s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
486		w.Header().Set("Location", s.url("/accounts/1"))
487		w.WriteHeader(http.StatusOK)
488		w.Write([]byte(`{"status": "valid"}`))
489	})
490	s.handle("/acme/new-order", func(w http.ResponseWriter, r *http.Request) {
491		w.Header().Set("Location", s.url("/orders/1"))
492		w.WriteHeader(http.StatusCreated)
493		fmt.Fprintf(w, `{
494			"status": "pending",
495			"expires": "2019-09-01T00:00:00Z",
496			"notBefore": "2019-08-31T00:00:00Z",
497			"notAfter": "2019-09-02T00:00:00Z",
498			"identifiers": [{"type":"dns", "value":"example.org"}],
499			"authorizations": [%q]
500		}`, s.url("/authz/1"))
501	})
502	s.start()
503	defer s.close()
504
505	cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
506	o, err := cl.AuthorizeOrder(context.Background(), DomainIDs("example.org"),
507		WithOrderNotBefore(time.Date(2019, 8, 31, 0, 0, 0, 0, time.UTC)),
508		WithOrderNotAfter(time.Date(2019, 9, 2, 0, 0, 0, 0, time.UTC)),
509	)
510	if err != nil {
511		t.Fatal(err)
512	}
513	okOrder := &Order{
514		URI:         s.url("/orders/1"),
515		Status:      StatusPending,
516		Expires:     time.Date(2019, 9, 1, 0, 0, 0, 0, time.UTC),
517		NotBefore:   time.Date(2019, 8, 31, 0, 0, 0, 0, time.UTC),
518		NotAfter:    time.Date(2019, 9, 2, 0, 0, 0, 0, time.UTC),
519		Identifiers: []AuthzID{AuthzID{Type: "dns", Value: "example.org"}},
520		AuthzURLs:   []string{s.url("/authz/1")},
521	}
522	if !reflect.DeepEqual(o, okOrder) {
523		t.Errorf("AuthorizeOrder = %+v; want %+v", o, okOrder)
524	}
525}
526
527func TestRFC_GetOrder(t *testing.T) {
528	s := newACMEServer()
529	s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
530		w.Header().Set("Location", s.url("/accounts/1"))
531		w.WriteHeader(http.StatusOK)
532		w.Write([]byte(`{"status": "valid"}`))
533	})
534	s.handle("/orders/1", func(w http.ResponseWriter, r *http.Request) {
535		w.Header().Set("Location", s.url("/orders/1"))
536		w.WriteHeader(http.StatusOK)
537		w.Write([]byte(`{
538			"status": "invalid",
539			"expires": "2019-09-01T00:00:00Z",
540			"notBefore": "2019-08-31T00:00:00Z",
541			"notAfter": "2019-09-02T00:00:00Z",
542			"identifiers": [{"type":"dns", "value":"example.org"}],
543			"authorizations": ["/authz/1"],
544			"finalize": "/orders/1/fin",
545			"certificate": "/orders/1/cert",
546			"error": {"type": "badRequest"}
547		}`))
548	})
549	s.start()
550	defer s.close()
551
552	cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
553	o, err := cl.GetOrder(context.Background(), s.url("/orders/1"))
554	if err != nil {
555		t.Fatal(err)
556	}
557	okOrder := &Order{
558		URI:         s.url("/orders/1"),
559		Status:      StatusInvalid,
560		Expires:     time.Date(2019, 9, 1, 0, 0, 0, 0, time.UTC),
561		NotBefore:   time.Date(2019, 8, 31, 0, 0, 0, 0, time.UTC),
562		NotAfter:    time.Date(2019, 9, 2, 0, 0, 0, 0, time.UTC),
563		Identifiers: []AuthzID{AuthzID{Type: "dns", Value: "example.org"}},
564		AuthzURLs:   []string{"/authz/1"},
565		FinalizeURL: "/orders/1/fin",
566		CertURL:     "/orders/1/cert",
567		Error:       &Error{ProblemType: "badRequest"},
568	}
569	if !reflect.DeepEqual(o, okOrder) {
570		t.Errorf("GetOrder = %+v\nwant %+v", o, okOrder)
571	}
572}
573
574func TestRFC_WaitOrder(t *testing.T) {
575	for _, st := range []string{StatusReady, StatusValid} {
576		t.Run(st, func(t *testing.T) {
577			testWaitOrderStatus(t, st)
578		})
579	}
580}
581
582func testWaitOrderStatus(t *testing.T, okStatus string) {
583	s := newACMEServer()
584	s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
585		w.Header().Set("Location", s.url("/accounts/1"))
586		w.WriteHeader(http.StatusOK)
587		w.Write([]byte(`{"status": "valid"}`))
588	})
589	var count int
590	s.handle("/orders/1", func(w http.ResponseWriter, r *http.Request) {
591		w.Header().Set("Location", s.url("/orders/1"))
592		w.WriteHeader(http.StatusOK)
593		s := StatusPending
594		if count > 0 {
595			s = okStatus
596		}
597		fmt.Fprintf(w, `{"status": %q}`, s)
598		count++
599	})
600	s.start()
601	defer s.close()
602
603	var order *Order
604	var err error
605	done := make(chan struct{})
606	go func() {
607		cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
608		order, err = cl.WaitOrder(context.Background(), s.url("/orders/1"))
609		close(done)
610	}()
611	select {
612	case <-time.After(3 * time.Second):
613		t.Fatal("WaitOrder took too long to return")
614	case <-done:
615		if err != nil {
616			t.Fatalf("WaitOrder: %v", err)
617		}
618		if order.Status != okStatus {
619			t.Errorf("order.Status = %q; want %q", order.Status, okStatus)
620		}
621	}
622}
623
624func TestRFC_WaitOrderError(t *testing.T) {
625	s := newACMEServer()
626	s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
627		w.Header().Set("Location", s.url("/accounts/1"))
628		w.WriteHeader(http.StatusOK)
629		w.Write([]byte(`{"status": "valid"}`))
630	})
631	var count int
632	s.handle("/orders/1", func(w http.ResponseWriter, r *http.Request) {
633		w.Header().Set("Location", s.url("/orders/1"))
634		w.WriteHeader(http.StatusOK)
635		s := StatusPending
636		if count > 0 {
637			s = StatusInvalid
638		}
639		fmt.Fprintf(w, `{"status": %q}`, s)
640		count++
641	})
642	s.start()
643	defer s.close()
644
645	var err error
646	done := make(chan struct{})
647	go func() {
648		cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
649		_, err = cl.WaitOrder(context.Background(), s.url("/orders/1"))
650		close(done)
651	}()
652	select {
653	case <-time.After(3 * time.Second):
654		t.Fatal("WaitOrder took too long to return")
655	case <-done:
656		if err == nil {
657			t.Fatal("WaitOrder returned nil error")
658		}
659		e, ok := err.(*OrderError)
660		if !ok {
661			t.Fatalf("err = %v (%T); want OrderError", err, err)
662		}
663		if e.OrderURL != s.url("/orders/1") {
664			t.Errorf("e.OrderURL = %q; want %q", e.OrderURL, s.url("/orders/1"))
665		}
666		if e.Status != StatusInvalid {
667			t.Errorf("e.Status = %q; want %q", e.Status, StatusInvalid)
668		}
669	}
670}
671
672func TestRFC_CreateOrderCert(t *testing.T) {
673	q := &x509.CertificateRequest{
674		Subject: pkix.Name{CommonName: "example.org"},
675	}
676	csr, err := x509.CreateCertificateRequest(rand.Reader, q, testKeyEC)
677	if err != nil {
678		t.Fatal(err)
679	}
680
681	tmpl := &x509.Certificate{SerialNumber: big.NewInt(1)}
682	leaf, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &testKeyEC.PublicKey, testKeyEC)
683	if err != nil {
684		t.Fatal(err)
685	}
686
687	s := newACMEServer()
688	s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
689		w.Header().Set("Location", s.url("/accounts/1"))
690		w.Write([]byte(`{"status": "valid"}`))
691	})
692	var count int
693	s.handle("/pleaseissue", func(w http.ResponseWriter, r *http.Request) {
694		w.Header().Set("Location", s.url("/pleaseissue"))
695		st := StatusProcessing
696		if count > 0 {
697			st = StatusValid
698		}
699		fmt.Fprintf(w, `{"status":%q, "certificate":%q}`, st, s.url("/crt"))
700		count++
701	})
702	s.handle("/crt", func(w http.ResponseWriter, r *http.Request) {
703		w.Header().Set("Content-Type", "application/pem-certificate-chain")
704		pem.Encode(w, &pem.Block{Type: "CERTIFICATE", Bytes: leaf})
705	})
706	s.start()
707	defer s.close()
708	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
709	defer cancel()
710
711	cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
712	cert, curl, err := cl.CreateOrderCert(ctx, s.url("/pleaseissue"), csr, true)
713	if err != nil {
714		t.Fatalf("CreateOrderCert: %v", err)
715	}
716	if _, err := x509.ParseCertificate(cert[0]); err != nil {
717		t.Errorf("ParseCertificate: %v", err)
718	}
719	if !reflect.DeepEqual(cert[0], leaf) {
720		t.Errorf("cert and leaf bytes don't match")
721	}
722	if u := s.url("/crt"); curl != u {
723		t.Errorf("curl = %q; want %q", curl, u)
724	}
725}
726
727func TestRFC_AlreadyRevokedCert(t *testing.T) {
728	s := newACMEServer()
729	s.handle("/acme/revoke-cert", func(w http.ResponseWriter, r *http.Request) {
730		s.error(w, &wireError{
731			Status: http.StatusBadRequest,
732			Type:   "urn:ietf:params:acme:error:alreadyRevoked",
733		})
734	})
735	s.start()
736	defer s.close()
737
738	cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
739	err := cl.RevokeCert(context.Background(), testKeyEC, []byte{0}, CRLReasonUnspecified)
740	if err != nil {
741		t.Fatalf("RevokeCert: %v", err)
742	}
743}
744