gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

rfc8555.go (14354B)


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