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	"context"
9	"crypto"
10	"encoding/base64"
11	"encoding/json"
12	"encoding/pem"
13	"errors"
14	"fmt"
15	"io"
16	"io/ioutil"
17	"net/http"
18	"time"
19)
20
21// DeactivateReg permanently disables an existing account associated with c.Key.
22// A deactivated account can no longer request certificate issuance or access
23// resources related to the account, such as orders or authorizations.
24//
25// It only works with CAs implementing RFC 8555.
26func (c *Client) DeactivateReg(ctx context.Context) error {
27	url := string(c.accountKID(ctx))
28	if url == "" {
29		return ErrNoAccount
30	}
31	req := json.RawMessage(`{"status": "deactivated"}`)
32	res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
33	if err != nil {
34		return err
35	}
36	res.Body.Close()
37	return nil
38}
39
40// registerRFC is quivalent to c.Register but for CAs implementing RFC 8555.
41// It expects c.Discover to have already been called.
42// TODO: Implement externalAccountBinding.
43func (c *Client) registerRFC(ctx context.Context, acct *Account, prompt func(tosURL string) bool) (*Account, error) {
44	c.cacheMu.Lock() // guard c.kid access
45	defer c.cacheMu.Unlock()
46
47	req := struct {
48		TermsAgreed bool     `json:"termsOfServiceAgreed,omitempty"`
49		Contact     []string `json:"contact,omitempty"`
50	}{
51		Contact: acct.Contact,
52	}
53	if c.dir.Terms != "" {
54		req.TermsAgreed = prompt(c.dir.Terms)
55	}
56	res, err := c.post(ctx, c.Key, c.dir.RegURL, req, wantStatus(
57		http.StatusOK,      // account with this key already registered
58		http.StatusCreated, // new account created
59	))
60	if err != nil {
61		return nil, err
62	}
63
64	defer res.Body.Close()
65	a, err := responseAccount(res)
66	if err != nil {
67		return nil, err
68	}
69	// Cache Account URL even if we return an error to the caller.
70	// It is by all means a valid and usable "kid" value for future requests.
71	c.kid = keyID(a.URI)
72	if res.StatusCode == http.StatusOK {
73		return nil, ErrAccountAlreadyExists
74	}
75	return a, nil
76}
77
78// updateGegRFC is equivalent to c.UpdateReg but for CAs implementing RFC 8555.
79// It expects c.Discover to have already been called.
80func (c *Client) updateRegRFC(ctx context.Context, a *Account) (*Account, error) {
81	url := string(c.accountKID(ctx))
82	if url == "" {
83		return nil, ErrNoAccount
84	}
85	req := struct {
86		Contact []string `json:"contact,omitempty"`
87	}{
88		Contact: a.Contact,
89	}
90	res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
91	if err != nil {
92		return nil, err
93	}
94	defer res.Body.Close()
95	return responseAccount(res)
96}
97
98// getGegRFC is equivalent to c.GetReg but for CAs implementing RFC 8555.
99// It expects c.Discover to have already been called.
100func (c *Client) getRegRFC(ctx context.Context) (*Account, error) {
101	req := json.RawMessage(`{"onlyReturnExisting": true}`)
102	res, err := c.post(ctx, c.Key, c.dir.RegURL, req, wantStatus(http.StatusOK))
103	if e, ok := err.(*Error); ok && e.ProblemType == "urn:ietf:params:acme:error:accountDoesNotExist" {
104		return nil, ErrNoAccount
105	}
106	if err != nil {
107		return nil, err
108	}
109
110	defer res.Body.Close()
111	return responseAccount(res)
112}
113
114func responseAccount(res *http.Response) (*Account, error) {
115	var v struct {
116		Status  string
117		Contact []string
118		Orders  string
119	}
120	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
121		return nil, fmt.Errorf("acme: invalid account response: %v", err)
122	}
123	return &Account{
124		URI:       res.Header.Get("Location"),
125		Status:    v.Status,
126		Contact:   v.Contact,
127		OrdersURL: v.Orders,
128	}, nil
129}
130
131// AuthorizeOrder initiates the order-based application for certificate issuance,
132// as opposed to pre-authorization in Authorize.
133// It is only supported by CAs implementing RFC 8555.
134//
135// The caller then needs to fetch each authorization with GetAuthorization,
136// identify those with StatusPending status and fulfill a challenge using Accept.
137// Once all authorizations are satisfied, the caller will typically want to poll
138// order status using WaitOrder until it's in StatusReady state.
139// To finalize the order and obtain a certificate, the caller submits a CSR with CreateOrderCert.
140func (c *Client) AuthorizeOrder(ctx context.Context, id []AuthzID, opt ...OrderOption) (*Order, error) {
141	dir, err := c.Discover(ctx)
142	if err != nil {
143		return nil, err
144	}
145
146	req := struct {
147		Identifiers []wireAuthzID `json:"identifiers"`
148		NotBefore   string        `json:"notBefore,omitempty"`
149		NotAfter    string        `json:"notAfter,omitempty"`
150	}{}
151	for _, v := range id {
152		req.Identifiers = append(req.Identifiers, wireAuthzID{
153			Type:  v.Type,
154			Value: v.Value,
155		})
156	}
157	for _, o := range opt {
158		switch o := o.(type) {
159		case orderNotBeforeOpt:
160			req.NotBefore = time.Time(o).Format(time.RFC3339)
161		case orderNotAfterOpt:
162			req.NotAfter = time.Time(o).Format(time.RFC3339)
163		default:
164			// Package's fault if we let this happen.
165			panic(fmt.Sprintf("unsupported order option type %T", o))
166		}
167	}
168
169	res, err := c.post(ctx, nil, dir.OrderURL, req, wantStatus(http.StatusCreated))
170	if err != nil {
171		return nil, err
172	}
173	defer res.Body.Close()
174	return responseOrder(res)
175}
176
177// GetOrder retrives an order identified by the given URL.
178// For orders created with AuthorizeOrder, the url value is Order.URI.
179//
180// If a caller needs to poll an order until its status is final,
181// see the WaitOrder method.
182func (c *Client) GetOrder(ctx context.Context, url string) (*Order, error) {
183	if _, err := c.Discover(ctx); err != nil {
184		return nil, err
185	}
186
187	res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
188	if err != nil {
189		return nil, err
190	}
191	defer res.Body.Close()
192	return responseOrder(res)
193}
194
195// WaitOrder polls an order from the given URL until it is in one of the final states,
196// StatusReady, StatusValid or StatusInvalid, the CA responded with a non-retryable error
197// or the context is done.
198//
199// It returns a non-nil Order only if its Status is StatusReady or StatusValid.
200// In all other cases WaitOrder returns an error.
201// If the Status is StatusInvalid, the returned error is of type *OrderError.
202func (c *Client) WaitOrder(ctx context.Context, url string) (*Order, error) {
203	if _, err := c.Discover(ctx); err != nil {
204		return nil, err
205	}
206	for {
207		res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
208		if err != nil {
209			return nil, err
210		}
211		o, err := responseOrder(res)
212		res.Body.Close()
213		switch {
214		case err != nil:
215			// Skip and retry.
216		case o.Status == StatusInvalid:
217			return nil, &OrderError{OrderURL: o.URI, Status: o.Status}
218		case o.Status == StatusReady || o.Status == StatusValid:
219			return o, nil
220		}
221
222		d := retryAfter(res.Header.Get("Retry-After"))
223		if d == 0 {
224			// Default retry-after.
225			// Same reasoning as in WaitAuthorization.
226			d = time.Second
227		}
228		t := time.NewTimer(d)
229		select {
230		case <-ctx.Done():
231			t.Stop()
232			return nil, ctx.Err()
233		case <-t.C:
234			// Retry.
235		}
236	}
237}
238
239func responseOrder(res *http.Response) (*Order, error) {
240	var v struct {
241		Status         string
242		Expires        time.Time
243		Identifiers    []wireAuthzID
244		NotBefore      time.Time
245		NotAfter       time.Time
246		Error          *wireError
247		Authorizations []string
248		Finalize       string
249		Certificate    string
250	}
251	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
252		return nil, fmt.Errorf("acme: error reading order: %v", err)
253	}
254	o := &Order{
255		URI:         res.Header.Get("Location"),
256		Status:      v.Status,
257		Expires:     v.Expires,
258		NotBefore:   v.NotBefore,
259		NotAfter:    v.NotAfter,
260		AuthzURLs:   v.Authorizations,
261		FinalizeURL: v.Finalize,
262		CertURL:     v.Certificate,
263	}
264	for _, id := range v.Identifiers {
265		o.Identifiers = append(o.Identifiers, AuthzID{Type: id.Type, Value: id.Value})
266	}
267	if v.Error != nil {
268		o.Error = v.Error.error(nil /* headers */)
269	}
270	return o, nil
271}
272
273// CreateOrderCert submits the CSR (Certificate Signing Request) to a CA at the specified URL.
274// The URL is the FinalizeURL field of an Order created with AuthorizeOrder.
275//
276// If the bundle argument is true, the returned value also contain the CA (issuer)
277// certificate chain. Otherwise, only a leaf certificate is returned.
278// The returned URL can be used to re-fetch the certificate using FetchCert.
279//
280// This method is only supported by CAs implementing RFC 8555. See CreateCert for pre-RFC CAs.
281//
282// CreateOrderCert returns an error if the CA's response is unreasonably large.
283// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
284func (c *Client) CreateOrderCert(ctx context.Context, url string, csr []byte, bundle bool) (der [][]byte, certURL string, err error) {
285	if _, err := c.Discover(ctx); err != nil { // required by c.accountKID
286		return nil, "", err
287	}
288
289	// RFC describes this as "finalize order" request.
290	req := struct {
291		CSR string `json:"csr"`
292	}{
293		CSR: base64.RawURLEncoding.EncodeToString(csr),
294	}
295	res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
296	if err != nil {
297		return nil, "", err
298	}
299	defer res.Body.Close()
300	o, err := responseOrder(res)
301	if err != nil {
302		return nil, "", err
303	}
304
305	// Wait for CA to issue the cert if they haven't.
306	if o.Status != StatusValid {
307		o, err = c.WaitOrder(ctx, o.URI)
308	}
309	if err != nil {
310		return nil, "", err
311	}
312	// The only acceptable status post finalize and WaitOrder is "valid".
313	if o.Status != StatusValid {
314		return nil, "", &OrderError{OrderURL: o.URI, Status: o.Status}
315	}
316	crt, err := c.fetchCertRFC(ctx, o.CertURL, bundle)
317	return crt, o.CertURL, err
318}
319
320// fetchCertRFC downloads issued certificate from the given URL.
321// It expects the CA to respond with PEM-encoded certificate chain.
322//
323// The URL argument is the CertURL field of Order.
324func (c *Client) fetchCertRFC(ctx context.Context, url string, bundle bool) ([][]byte, error) {
325	res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
326	if err != nil {
327		return nil, err
328	}
329	defer res.Body.Close()
330
331	// Get all the bytes up to a sane maximum.
332	// Account very roughly for base64 overhead.
333	const max = maxCertChainSize + maxCertChainSize/33
334	b, err := ioutil.ReadAll(io.LimitReader(res.Body, max+1))
335	if err != nil {
336		return nil, fmt.Errorf("acme: fetch cert response stream: %v", err)
337	}
338	if len(b) > max {
339		return nil, errors.New("acme: certificate chain is too big")
340	}
341
342	// Decode PEM chain.
343	var chain [][]byte
344	for {
345		var p *pem.Block
346		p, b = pem.Decode(b)
347		if p == nil {
348			break
349		}
350		if p.Type != "CERTIFICATE" {
351			return nil, fmt.Errorf("acme: invalid PEM cert type %q", p.Type)
352		}
353
354		chain = append(chain, p.Bytes)
355		if !bundle {
356			return chain, nil
357		}
358		if len(chain) > maxChainLen {
359			return nil, errors.New("acme: certificate chain is too long")
360		}
361	}
362	if len(chain) == 0 {
363		return nil, errors.New("acme: certificate chain is empty")
364	}
365	return chain, nil
366}
367
368// sends a cert revocation request in either JWK form when key is non-nil or KID form otherwise.
369func (c *Client) revokeCertRFC(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error {
370	req := &struct {
371		Cert   string `json:"certificate"`
372		Reason int    `json:"reason"`
373	}{
374		Cert:   base64.RawURLEncoding.EncodeToString(cert),
375		Reason: int(reason),
376	}
377	res, err := c.post(ctx, key, c.dir.RevokeURL, req, wantStatus(http.StatusOK))
378	if err != nil {
379		if isAlreadyRevoked(err) {
380			// Assume it is not an error to revoke an already revoked cert.
381			return nil
382		}
383		return err
384	}
385	defer res.Body.Close()
386	return nil
387}
388
389func isAlreadyRevoked(err error) bool {
390	e, ok := err.(*Error)
391	return ok && e.ProblemType == "urn:ietf:params:acme:error:alreadyRevoked"
392}
393