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