...

Source file src/golang.org/x/crypto/acme/autocert/internal/acmetest/ca.go

Documentation: golang.org/x/crypto/acme/autocert/internal/acmetest

     1  // Copyright 2018 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 acmetest provides types for testing acme and autocert packages.
     6  //
     7  // TODO: Consider moving this to x/crypto/acme/internal/acmetest for acme tests as well.
     8  package acmetest
     9  
    10  import (
    11  	"context"
    12  	"crypto"
    13  	"crypto/ecdsa"
    14  	"crypto/elliptic"
    15  	"crypto/rand"
    16  	"crypto/rsa"
    17  	"crypto/tls"
    18  	"crypto/x509"
    19  	"crypto/x509/pkix"
    20  	"encoding/asn1"
    21  	"encoding/base64"
    22  	"encoding/json"
    23  	"encoding/pem"
    24  	"fmt"
    25  	"io"
    26  	"math/big"
    27  	"net"
    28  	"net/http"
    29  	"net/http/httptest"
    30  	"path"
    31  	"strconv"
    32  	"strings"
    33  	"sync"
    34  	"testing"
    35  	"time"
    36  
    37  	"golang.org/x/crypto/acme"
    38  )
    39  
    40  // CAServer is a simple test server which implements ACME spec bits needed for testing.
    41  type CAServer struct {
    42  	rootKey      crypto.Signer
    43  	rootCert     []byte // DER encoding
    44  	rootTemplate *x509.Certificate
    45  
    46  	t              *testing.T
    47  	server         *httptest.Server
    48  	issuer         pkix.Name
    49  	challengeTypes []string
    50  	url            string
    51  	roots          *x509.CertPool
    52  	eabRequired    bool
    53  
    54  	mu             sync.Mutex
    55  	certCount      int                           // number of issued certs
    56  	acctRegistered bool                          // set once an account has been registered
    57  	domainAddr     map[string]string             // domain name to addr:port resolution
    58  	domainGetCert  map[string]getCertificateFunc // domain name to GetCertificate function
    59  	domainHandler  map[string]http.Handler       // domain name to Handle function
    60  	validAuthz     map[string]*authorization     // valid authz, keyed by domain name
    61  	authorizations []*authorization              // all authz, index is used as ID
    62  	orders         []*order                      // index is used as order ID
    63  	errors         []error                       // encountered client errors
    64  }
    65  
    66  type getCertificateFunc func(hello *tls.ClientHelloInfo) (*tls.Certificate, error)
    67  
    68  // NewCAServer creates a new ACME test server. The returned CAServer issues
    69  // certs signed with the CA roots available in the Roots field.
    70  func NewCAServer(t *testing.T) *CAServer {
    71  	ca := &CAServer{t: t,
    72  		challengeTypes: []string{"fake-01", "tls-alpn-01", "http-01"},
    73  		domainAddr:     make(map[string]string),
    74  		domainGetCert:  make(map[string]getCertificateFunc),
    75  		domainHandler:  make(map[string]http.Handler),
    76  		validAuthz:     make(map[string]*authorization),
    77  	}
    78  
    79  	ca.server = httptest.NewUnstartedServer(http.HandlerFunc(ca.handle))
    80  
    81  	r, err := rand.Int(rand.Reader, big.NewInt(1000000))
    82  	if err != nil {
    83  		panic(fmt.Sprintf("rand.Int: %v", err))
    84  	}
    85  	ca.issuer = pkix.Name{
    86  		Organization: []string{"Test Acme Co"},
    87  		CommonName:   "Root CA " + r.String(),
    88  	}
    89  
    90  	return ca
    91  }
    92  
    93  func (ca *CAServer) generateRoot() {
    94  	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    95  	if err != nil {
    96  		panic(fmt.Sprintf("ecdsa.GenerateKey: %v", err))
    97  	}
    98  	tmpl := &x509.Certificate{
    99  		SerialNumber:          big.NewInt(1),
   100  		Subject:               ca.issuer,
   101  		NotBefore:             time.Now(),
   102  		NotAfter:              time.Now().Add(365 * 24 * time.Hour),
   103  		KeyUsage:              x509.KeyUsageCertSign,
   104  		BasicConstraintsValid: true,
   105  		IsCA:                  true,
   106  	}
   107  	der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key)
   108  	if err != nil {
   109  		panic(fmt.Sprintf("x509.CreateCertificate: %v", err))
   110  	}
   111  	cert, err := x509.ParseCertificate(der)
   112  	if err != nil {
   113  		panic(fmt.Sprintf("x509.ParseCertificate: %v", err))
   114  	}
   115  	ca.roots = x509.NewCertPool()
   116  	ca.roots.AddCert(cert)
   117  	ca.rootKey = key
   118  	ca.rootCert = der
   119  	ca.rootTemplate = tmpl
   120  }
   121  
   122  // IssuerName sets the name of the issuing CA.
   123  func (ca *CAServer) IssuerName(name pkix.Name) *CAServer {
   124  	if ca.url != "" {
   125  		panic("IssuerName must be called before Start")
   126  	}
   127  	ca.issuer = name
   128  	return ca
   129  }
   130  
   131  // ChallengeTypes sets the supported challenge types.
   132  func (ca *CAServer) ChallengeTypes(types ...string) *CAServer {
   133  	if ca.url != "" {
   134  		panic("ChallengeTypes must be called before Start")
   135  	}
   136  	ca.challengeTypes = types
   137  	return ca
   138  }
   139  
   140  // URL returns the server address, after Start has been called.
   141  func (ca *CAServer) URL() string {
   142  	if ca.url == "" {
   143  		panic("URL called before Start")
   144  	}
   145  	return ca.url
   146  }
   147  
   148  // Roots returns a pool cointaining the CA root.
   149  func (ca *CAServer) Roots() *x509.CertPool {
   150  	if ca.url == "" {
   151  		panic("Roots called before Start")
   152  	}
   153  	return ca.roots
   154  }
   155  
   156  // ExternalAccountRequired makes an EAB JWS required for account registration.
   157  func (ca *CAServer) ExternalAccountRequired() *CAServer {
   158  	if ca.url != "" {
   159  		panic("ExternalAccountRequired must be called before Start")
   160  	}
   161  	ca.eabRequired = true
   162  	return ca
   163  }
   164  
   165  // Start starts serving requests. The server address becomes available in the
   166  // URL field.
   167  func (ca *CAServer) Start() *CAServer {
   168  	if ca.url == "" {
   169  		ca.generateRoot()
   170  		ca.server.Start()
   171  		ca.t.Cleanup(ca.server.Close)
   172  		ca.url = ca.server.URL
   173  	}
   174  	return ca
   175  }
   176  
   177  func (ca *CAServer) serverURL(format string, arg ...interface{}) string {
   178  	return ca.server.URL + fmt.Sprintf(format, arg...)
   179  }
   180  
   181  func (ca *CAServer) addr(domain string) (string, bool) {
   182  	ca.mu.Lock()
   183  	defer ca.mu.Unlock()
   184  	addr, ok := ca.domainAddr[domain]
   185  	return addr, ok
   186  }
   187  
   188  func (ca *CAServer) getCert(domain string) (getCertificateFunc, bool) {
   189  	ca.mu.Lock()
   190  	defer ca.mu.Unlock()
   191  	f, ok := ca.domainGetCert[domain]
   192  	return f, ok
   193  }
   194  
   195  func (ca *CAServer) getHandler(domain string) (http.Handler, bool) {
   196  	ca.mu.Lock()
   197  	defer ca.mu.Unlock()
   198  	h, ok := ca.domainHandler[domain]
   199  	return h, ok
   200  }
   201  
   202  func (ca *CAServer) httpErrorf(w http.ResponseWriter, code int, format string, a ...interface{}) {
   203  	s := fmt.Sprintf(format, a...)
   204  	ca.t.Errorf(format, a...)
   205  	http.Error(w, s, code)
   206  }
   207  
   208  // Resolve adds a domain to address resolution for the ca to dial to
   209  // when validating challenges for the domain authorization.
   210  func (ca *CAServer) Resolve(domain, addr string) {
   211  	ca.mu.Lock()
   212  	defer ca.mu.Unlock()
   213  	ca.domainAddr[domain] = addr
   214  }
   215  
   216  // ResolveGetCertificate redirects TLS connections for domain to f when
   217  // validating challenges for the domain authorization.
   218  func (ca *CAServer) ResolveGetCertificate(domain string, f getCertificateFunc) {
   219  	ca.mu.Lock()
   220  	defer ca.mu.Unlock()
   221  	ca.domainGetCert[domain] = f
   222  }
   223  
   224  // ResolveHandler redirects HTTP requests for domain to f when
   225  // validating challenges for the domain authorization.
   226  func (ca *CAServer) ResolveHandler(domain string, h http.Handler) {
   227  	ca.mu.Lock()
   228  	defer ca.mu.Unlock()
   229  	ca.domainHandler[domain] = h
   230  }
   231  
   232  type discovery struct {
   233  	NewNonce   string `json:"newNonce"`
   234  	NewAccount string `json:"newAccount"`
   235  	NewOrder   string `json:"newOrder"`
   236  	NewAuthz   string `json:"newAuthz"`
   237  
   238  	Meta discoveryMeta `json:"meta,omitempty"`
   239  }
   240  
   241  type discoveryMeta struct {
   242  	ExternalAccountRequired bool `json:"externalAccountRequired,omitempty"`
   243  }
   244  
   245  type challenge struct {
   246  	URI   string `json:"uri"`
   247  	Type  string `json:"type"`
   248  	Token string `json:"token"`
   249  }
   250  
   251  type authorization struct {
   252  	Status     string      `json:"status"`
   253  	Challenges []challenge `json:"challenges"`
   254  
   255  	domain string
   256  	id     int
   257  }
   258  
   259  type order struct {
   260  	Status      string   `json:"status"`
   261  	AuthzURLs   []string `json:"authorizations"`
   262  	FinalizeURL string   `json:"finalize"`    // CSR submit URL
   263  	CertURL     string   `json:"certificate"` // already issued cert
   264  
   265  	leaf []byte // issued cert in DER format
   266  }
   267  
   268  func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
   269  	ca.t.Logf("%s %s", r.Method, r.URL)
   270  	w.Header().Set("Replay-Nonce", "nonce")
   271  	// TODO: Verify nonce header for all POST requests.
   272  
   273  	switch {
   274  	default:
   275  		ca.httpErrorf(w, http.StatusBadRequest, "unrecognized r.URL.Path: %s", r.URL.Path)
   276  
   277  	// Discovery request.
   278  	case r.URL.Path == "/":
   279  		resp := &discovery{
   280  			NewNonce:   ca.serverURL("/new-nonce"),
   281  			NewAccount: ca.serverURL("/new-account"),
   282  			NewOrder:   ca.serverURL("/new-order"),
   283  			Meta: discoveryMeta{
   284  				ExternalAccountRequired: ca.eabRequired,
   285  			},
   286  		}
   287  		if err := json.NewEncoder(w).Encode(resp); err != nil {
   288  			panic(fmt.Sprintf("discovery response: %v", err))
   289  		}
   290  
   291  	// Nonce requests.
   292  	case r.URL.Path == "/new-nonce":
   293  		// Nonce values are always set. Nothing else to do.
   294  		return
   295  
   296  	// Client key registration request.
   297  	case r.URL.Path == "/new-account":
   298  		ca.mu.Lock()
   299  		defer ca.mu.Unlock()
   300  		if ca.acctRegistered {
   301  			ca.httpErrorf(w, http.StatusServiceUnavailable, "multiple accounts are not implemented")
   302  			return
   303  		}
   304  		ca.acctRegistered = true
   305  
   306  		var req struct {
   307  			ExternalAccountBinding json.RawMessage
   308  		}
   309  
   310  		if err := decodePayload(&req, r.Body); err != nil {
   311  			ca.httpErrorf(w, http.StatusBadRequest, err.Error())
   312  			return
   313  		}
   314  
   315  		if ca.eabRequired && len(req.ExternalAccountBinding) == 0 {
   316  			ca.httpErrorf(w, http.StatusBadRequest, "registration failed: no JWS for EAB")
   317  			return
   318  		}
   319  
   320  		// TODO: Check the user account key against a ca.accountKeys?
   321  		w.Header().Set("Location", ca.serverURL("/accounts/1"))
   322  		w.WriteHeader(http.StatusCreated)
   323  		w.Write([]byte("{}"))
   324  
   325  	// New order request.
   326  	case r.URL.Path == "/new-order":
   327  		var req struct {
   328  			Identifiers []struct{ Value string }
   329  		}
   330  		if err := decodePayload(&req, r.Body); err != nil {
   331  			ca.httpErrorf(w, http.StatusBadRequest, err.Error())
   332  			return
   333  		}
   334  		ca.mu.Lock()
   335  		defer ca.mu.Unlock()
   336  		o := &order{Status: acme.StatusPending}
   337  		for _, id := range req.Identifiers {
   338  			z := ca.authz(id.Value)
   339  			o.AuthzURLs = append(o.AuthzURLs, ca.serverURL("/authz/%d", z.id))
   340  		}
   341  		orderID := len(ca.orders)
   342  		ca.orders = append(ca.orders, o)
   343  		w.Header().Set("Location", ca.serverURL("/orders/%d", orderID))
   344  		w.WriteHeader(http.StatusCreated)
   345  		if err := json.NewEncoder(w).Encode(o); err != nil {
   346  			panic(err)
   347  		}
   348  
   349  	// Existing order status requests.
   350  	case strings.HasPrefix(r.URL.Path, "/orders/"):
   351  		ca.mu.Lock()
   352  		defer ca.mu.Unlock()
   353  		o, err := ca.storedOrder(strings.TrimPrefix(r.URL.Path, "/orders/"))
   354  		if err != nil {
   355  			ca.httpErrorf(w, http.StatusBadRequest, err.Error())
   356  			return
   357  		}
   358  		if err := json.NewEncoder(w).Encode(o); err != nil {
   359  			panic(err)
   360  		}
   361  
   362  	// Accept challenge requests.
   363  	case strings.HasPrefix(r.URL.Path, "/challenge/"):
   364  		parts := strings.Split(r.URL.Path, "/")
   365  		typ, id := parts[len(parts)-2], parts[len(parts)-1]
   366  		ca.mu.Lock()
   367  		supported := false
   368  		for _, suppTyp := range ca.challengeTypes {
   369  			if suppTyp == typ {
   370  				supported = true
   371  			}
   372  		}
   373  		a, err := ca.storedAuthz(id)
   374  		ca.mu.Unlock()
   375  		if !supported {
   376  			ca.httpErrorf(w, http.StatusBadRequest, "unsupported challenge: %v", typ)
   377  			return
   378  		}
   379  		if err != nil {
   380  			ca.httpErrorf(w, http.StatusBadRequest, "challenge accept: %v", err)
   381  			return
   382  		}
   383  		ca.validateChallenge(a, typ)
   384  		w.Write([]byte("{}"))
   385  
   386  	// Get authorization status requests.
   387  	case strings.HasPrefix(r.URL.Path, "/authz/"):
   388  		var req struct{ Status string }
   389  		decodePayload(&req, r.Body)
   390  		deactivate := req.Status == "deactivated"
   391  		ca.mu.Lock()
   392  		defer ca.mu.Unlock()
   393  		authz, err := ca.storedAuthz(strings.TrimPrefix(r.URL.Path, "/authz/"))
   394  		if err != nil {
   395  			ca.httpErrorf(w, http.StatusNotFound, "%v", err)
   396  			return
   397  		}
   398  		if deactivate {
   399  			// Note we don't invalidate authorized orders as we should.
   400  			authz.Status = "deactivated"
   401  			ca.t.Logf("authz %d is now %s", authz.id, authz.Status)
   402  			ca.updatePendingOrders()
   403  		}
   404  		if err := json.NewEncoder(w).Encode(authz); err != nil {
   405  			panic(fmt.Sprintf("encoding authz %d: %v", authz.id, err))
   406  		}
   407  
   408  	// Certificate issuance request.
   409  	case strings.HasPrefix(r.URL.Path, "/new-cert/"):
   410  		ca.mu.Lock()
   411  		defer ca.mu.Unlock()
   412  		orderID := strings.TrimPrefix(r.URL.Path, "/new-cert/")
   413  		o, err := ca.storedOrder(orderID)
   414  		if err != nil {
   415  			ca.httpErrorf(w, http.StatusBadRequest, err.Error())
   416  			return
   417  		}
   418  		if o.Status != acme.StatusReady {
   419  			ca.httpErrorf(w, http.StatusForbidden, "order status: %s", o.Status)
   420  			return
   421  		}
   422  		// Validate CSR request.
   423  		var req struct {
   424  			CSR string `json:"csr"`
   425  		}
   426  		decodePayload(&req, r.Body)
   427  		b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
   428  		csr, err := x509.ParseCertificateRequest(b)
   429  		if err != nil {
   430  			ca.httpErrorf(w, http.StatusBadRequest, err.Error())
   431  			return
   432  		}
   433  		// Issue the certificate.
   434  		der, err := ca.leafCert(csr)
   435  		if err != nil {
   436  			ca.httpErrorf(w, http.StatusBadRequest, "new-cert response: ca.leafCert: %v", err)
   437  			return
   438  		}
   439  		o.leaf = der
   440  		o.CertURL = ca.serverURL("/issued-cert/%s", orderID)
   441  		o.Status = acme.StatusValid
   442  		if err := json.NewEncoder(w).Encode(o); err != nil {
   443  			panic(err)
   444  		}
   445  
   446  	// Already issued cert download requests.
   447  	case strings.HasPrefix(r.URL.Path, "/issued-cert/"):
   448  		ca.mu.Lock()
   449  		defer ca.mu.Unlock()
   450  		o, err := ca.storedOrder(strings.TrimPrefix(r.URL.Path, "/issued-cert/"))
   451  		if err != nil {
   452  			ca.httpErrorf(w, http.StatusBadRequest, err.Error())
   453  			return
   454  		}
   455  		if o.Status != acme.StatusValid {
   456  			ca.httpErrorf(w, http.StatusForbidden, "order status: %s", o.Status)
   457  			return
   458  		}
   459  		w.Header().Set("Content-Type", "application/pem-certificate-chain")
   460  		pem.Encode(w, &pem.Block{Type: "CERTIFICATE", Bytes: o.leaf})
   461  		pem.Encode(w, &pem.Block{Type: "CERTIFICATE", Bytes: ca.rootCert})
   462  	}
   463  }
   464  
   465  // storedOrder retrieves a previously created order at index i.
   466  // It requires ca.mu to be locked.
   467  func (ca *CAServer) storedOrder(i string) (*order, error) {
   468  	idx, err := strconv.Atoi(i)
   469  	if err != nil {
   470  		return nil, fmt.Errorf("storedOrder: %v", err)
   471  	}
   472  	if idx < 0 {
   473  		return nil, fmt.Errorf("storedOrder: invalid order index %d", idx)
   474  	}
   475  	if idx > len(ca.orders)-1 {
   476  		return nil, fmt.Errorf("storedOrder: no such order %d", idx)
   477  	}
   478  
   479  	ca.updatePendingOrders()
   480  	return ca.orders[idx], nil
   481  }
   482  
   483  // storedAuthz retrieves a previously created authz at index i.
   484  // It requires ca.mu to be locked.
   485  func (ca *CAServer) storedAuthz(i string) (*authorization, error) {
   486  	idx, err := strconv.Atoi(i)
   487  	if err != nil {
   488  		return nil, fmt.Errorf("storedAuthz: %v", err)
   489  	}
   490  	if idx < 0 {
   491  		return nil, fmt.Errorf("storedAuthz: invalid authz index %d", idx)
   492  	}
   493  	if idx > len(ca.authorizations)-1 {
   494  		return nil, fmt.Errorf("storedAuthz: no such authz %d", idx)
   495  	}
   496  	return ca.authorizations[idx], nil
   497  }
   498  
   499  // authz returns an existing valid authorization for the identifier or creates a
   500  // new one. It requires ca.mu to be locked.
   501  func (ca *CAServer) authz(identifier string) *authorization {
   502  	authz, ok := ca.validAuthz[identifier]
   503  	if !ok {
   504  		authzId := len(ca.authorizations)
   505  		authz = &authorization{
   506  			id:     authzId,
   507  			domain: identifier,
   508  			Status: acme.StatusPending,
   509  		}
   510  		for _, typ := range ca.challengeTypes {
   511  			authz.Challenges = append(authz.Challenges, challenge{
   512  				Type:  typ,
   513  				URI:   ca.serverURL("/challenge/%s/%d", typ, authzId),
   514  				Token: challengeToken(authz.domain, typ, authzId),
   515  			})
   516  		}
   517  		ca.authorizations = append(ca.authorizations, authz)
   518  	}
   519  	return authz
   520  }
   521  
   522  // leafCert issues a new certificate.
   523  // It requires ca.mu to be locked.
   524  func (ca *CAServer) leafCert(csr *x509.CertificateRequest) (der []byte, err error) {
   525  	ca.certCount++ // next leaf cert serial number
   526  	leaf := &x509.Certificate{
   527  		SerialNumber:          big.NewInt(int64(ca.certCount)),
   528  		Subject:               pkix.Name{Organization: []string{"Test Acme Co"}},
   529  		NotBefore:             time.Now(),
   530  		NotAfter:              time.Now().Add(90 * 24 * time.Hour),
   531  		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   532  		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   533  		DNSNames:              csr.DNSNames,
   534  		BasicConstraintsValid: true,
   535  	}
   536  	if len(csr.DNSNames) == 0 {
   537  		leaf.DNSNames = []string{csr.Subject.CommonName}
   538  	}
   539  	return x509.CreateCertificate(rand.Reader, leaf, ca.rootTemplate, csr.PublicKey, ca.rootKey)
   540  }
   541  
   542  // LeafCert issues a leaf certificate.
   543  func (ca *CAServer) LeafCert(name, keyType string, notBefore, notAfter time.Time) *tls.Certificate {
   544  	if ca.url == "" {
   545  		panic("LeafCert called before Start")
   546  	}
   547  
   548  	ca.mu.Lock()
   549  	defer ca.mu.Unlock()
   550  	var pk crypto.Signer
   551  	switch keyType {
   552  	case "RSA":
   553  		var err error
   554  		pk, err = rsa.GenerateKey(rand.Reader, 1024)
   555  		if err != nil {
   556  			ca.t.Fatal(err)
   557  		}
   558  	case "ECDSA":
   559  		var err error
   560  		pk, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   561  		if err != nil {
   562  			ca.t.Fatal(err)
   563  		}
   564  	default:
   565  		panic("LeafCert: unknown key type")
   566  	}
   567  	ca.certCount++ // next leaf cert serial number
   568  	leaf := &x509.Certificate{
   569  		SerialNumber:          big.NewInt(int64(ca.certCount)),
   570  		Subject:               pkix.Name{Organization: []string{"Test Acme Co"}},
   571  		NotBefore:             notBefore,
   572  		NotAfter:              notAfter,
   573  		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   574  		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   575  		DNSNames:              []string{name},
   576  		BasicConstraintsValid: true,
   577  	}
   578  	der, err := x509.CreateCertificate(rand.Reader, leaf, ca.rootTemplate, pk.Public(), ca.rootKey)
   579  	if err != nil {
   580  		ca.t.Fatal(err)
   581  	}
   582  	return &tls.Certificate{
   583  		Certificate: [][]byte{der},
   584  		PrivateKey:  pk,
   585  	}
   586  }
   587  
   588  func (ca *CAServer) validateChallenge(authz *authorization, typ string) {
   589  	var err error
   590  	switch typ {
   591  	case "tls-alpn-01":
   592  		err = ca.verifyALPNChallenge(authz)
   593  	case "http-01":
   594  		err = ca.verifyHTTPChallenge(authz)
   595  	default:
   596  		panic(fmt.Sprintf("validation of %q is not implemented", typ))
   597  	}
   598  	ca.mu.Lock()
   599  	defer ca.mu.Unlock()
   600  	if err != nil {
   601  		authz.Status = "invalid"
   602  	} else {
   603  		authz.Status = "valid"
   604  		ca.validAuthz[authz.domain] = authz
   605  	}
   606  	ca.t.Logf("validated %q for %q, err: %v", typ, authz.domain, err)
   607  	ca.t.Logf("authz %d is now %s", authz.id, authz.Status)
   608  
   609  	ca.updatePendingOrders()
   610  }
   611  
   612  func (ca *CAServer) updatePendingOrders() {
   613  	// Update all pending orders.
   614  	// An order becomes "ready" if all authorizations are "valid".
   615  	// An order becomes "invalid" if any authorization is "invalid".
   616  	// Status changes: https://tools.ietf.org/html/rfc8555#section-7.1.6
   617  	for i, o := range ca.orders {
   618  		if o.Status != acme.StatusPending {
   619  			continue
   620  		}
   621  
   622  		countValid, countInvalid := ca.validateAuthzURLs(o.AuthzURLs, i)
   623  		if countInvalid > 0 {
   624  			o.Status = acme.StatusInvalid
   625  			ca.t.Logf("order %d is now invalid", i)
   626  			continue
   627  		}
   628  		if countValid == len(o.AuthzURLs) {
   629  			o.Status = acme.StatusReady
   630  			o.FinalizeURL = ca.serverURL("/new-cert/%d", i)
   631  			ca.t.Logf("order %d is now ready", i)
   632  		}
   633  	}
   634  }
   635  
   636  func (ca *CAServer) validateAuthzURLs(urls []string, orderNum int) (countValid, countInvalid int) {
   637  	for _, zurl := range urls {
   638  		z, err := ca.storedAuthz(path.Base(zurl))
   639  		if err != nil {
   640  			ca.t.Logf("no authz %q for order %d", zurl, orderNum)
   641  			continue
   642  		}
   643  		if z.Status == acme.StatusInvalid {
   644  			countInvalid++
   645  		}
   646  		if z.Status == acme.StatusValid {
   647  			countValid++
   648  		}
   649  	}
   650  	return countValid, countInvalid
   651  }
   652  
   653  func (ca *CAServer) verifyALPNChallenge(a *authorization) error {
   654  	const acmeALPNProto = "acme-tls/1"
   655  
   656  	addr, haveAddr := ca.addr(a.domain)
   657  	getCert, haveGetCert := ca.getCert(a.domain)
   658  	if !haveAddr && !haveGetCert {
   659  		return fmt.Errorf("no resolution information for %q", a.domain)
   660  	}
   661  	if haveAddr && haveGetCert {
   662  		return fmt.Errorf("overlapping resolution information for %q", a.domain)
   663  	}
   664  
   665  	var crt *x509.Certificate
   666  	switch {
   667  	case haveAddr:
   668  		conn, err := tls.Dial("tcp", addr, &tls.Config{
   669  			ServerName:         a.domain,
   670  			InsecureSkipVerify: true,
   671  			NextProtos:         []string{acmeALPNProto},
   672  			MinVersion:         tls.VersionTLS12,
   673  		})
   674  		if err != nil {
   675  			return err
   676  		}
   677  		if v := conn.ConnectionState().NegotiatedProtocol; v != acmeALPNProto {
   678  			return fmt.Errorf("CAServer: verifyALPNChallenge: negotiated proto is %q; want %q", v, acmeALPNProto)
   679  		}
   680  		if n := len(conn.ConnectionState().PeerCertificates); n != 1 {
   681  			return fmt.Errorf("len(PeerCertificates) = %d; want 1", n)
   682  		}
   683  		crt = conn.ConnectionState().PeerCertificates[0]
   684  	case haveGetCert:
   685  		hello := &tls.ClientHelloInfo{
   686  			ServerName: a.domain,
   687  			// TODO: support selecting ECDSA.
   688  			CipherSuites:      []uint16{tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305},
   689  			SupportedProtos:   []string{acme.ALPNProto},
   690  			SupportedVersions: []uint16{tls.VersionTLS12},
   691  		}
   692  		c, err := getCert(hello)
   693  		if err != nil {
   694  			return err
   695  		}
   696  		crt, err = x509.ParseCertificate(c.Certificate[0])
   697  		if err != nil {
   698  			return err
   699  		}
   700  	}
   701  
   702  	if err := crt.VerifyHostname(a.domain); err != nil {
   703  		return fmt.Errorf("verifyALPNChallenge: VerifyHostname: %v", err)
   704  	}
   705  	// See RFC 8737, Section 6.1.
   706  	oid := asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31}
   707  	for _, x := range crt.Extensions {
   708  		if x.Id.Equal(oid) {
   709  			// TODO: check the token.
   710  			return nil
   711  		}
   712  	}
   713  	return fmt.Errorf("verifyTokenCert: no id-pe-acmeIdentifier extension found")
   714  }
   715  
   716  func (ca *CAServer) verifyHTTPChallenge(a *authorization) error {
   717  	addr, haveAddr := ca.addr(a.domain)
   718  	handler, haveHandler := ca.getHandler(a.domain)
   719  	if !haveAddr && !haveHandler {
   720  		return fmt.Errorf("no resolution information for %q", a.domain)
   721  	}
   722  	if haveAddr && haveHandler {
   723  		return fmt.Errorf("overlapping resolution information for %q", a.domain)
   724  	}
   725  
   726  	token := challengeToken(a.domain, "http-01", a.id)
   727  	path := "/.well-known/acme-challenge/" + token
   728  
   729  	var body string
   730  	switch {
   731  	case haveAddr:
   732  		t := &http.Transport{
   733  			DialContext: func(ctx context.Context, network, _ string) (net.Conn, error) {
   734  				return (&net.Dialer{}).DialContext(ctx, network, addr)
   735  			},
   736  		}
   737  		req, err := http.NewRequest("GET", "http://"+a.domain+path, nil)
   738  		if err != nil {
   739  			return err
   740  		}
   741  		res, err := t.RoundTrip(req)
   742  		if err != nil {
   743  			return err
   744  		}
   745  		if res.StatusCode != http.StatusOK {
   746  			return fmt.Errorf("http token: w.Code = %d; want %d", res.StatusCode, http.StatusOK)
   747  		}
   748  		b, err := io.ReadAll(res.Body)
   749  		if err != nil {
   750  			return err
   751  		}
   752  		body = string(b)
   753  	case haveHandler:
   754  		r := httptest.NewRequest("GET", path, nil)
   755  		r.Host = a.domain
   756  		w := httptest.NewRecorder()
   757  		handler.ServeHTTP(w, r)
   758  		if w.Code != http.StatusOK {
   759  			return fmt.Errorf("http token: w.Code = %d; want %d", w.Code, http.StatusOK)
   760  		}
   761  		body = w.Body.String()
   762  	}
   763  
   764  	if !strings.HasPrefix(body, token) {
   765  		return fmt.Errorf("http token value = %q; want 'token-http-01.' prefix", body)
   766  	}
   767  	return nil
   768  }
   769  
   770  func decodePayload(v interface{}, r io.Reader) error {
   771  	var req struct{ Payload string }
   772  	if err := json.NewDecoder(r).Decode(&req); err != nil {
   773  		return err
   774  	}
   775  	payload, err := base64.RawURLEncoding.DecodeString(req.Payload)
   776  	if err != nil {
   777  		return err
   778  	}
   779  	return json.Unmarshal(payload, v)
   780  }
   781  
   782  func challengeToken(domain, challType string, authzID int) string {
   783  	return fmt.Sprintf("token-%s-%s-%d", domain, challType, authzID)
   784  }
   785  
   786  func unique(a []string) []string {
   787  	seen := make(map[string]bool)
   788  	var res []string
   789  	for _, s := range a {
   790  		if s != "" && !seen[s] {
   791  			seen[s] = true
   792  			res = append(res, s)
   793  		}
   794  	}
   795  	return res
   796  }
   797  

View as plain text