...

Source file src/golang.org/x/crypto/acme/acme_test.go

Documentation: golang.org/x/crypto/acme

     1  // Copyright 2015 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  	"bytes"
     9  	"context"
    10  	"crypto/rand"
    11  	"crypto/rsa"
    12  	"crypto/tls"
    13  	"crypto/x509"
    14  	"crypto/x509/pkix"
    15  	"encoding/base64"
    16  	"encoding/hex"
    17  	"encoding/json"
    18  	"fmt"
    19  	"io"
    20  	"math/big"
    21  	"net/http"
    22  	"net/http/httptest"
    23  	"reflect"
    24  	"sort"
    25  	"strings"
    26  	"testing"
    27  	"time"
    28  )
    29  
    30  // newTestClient creates a client with a non-nil Directory so that it skips
    31  // the discovery which is otherwise done on the first call of almost every
    32  // exported method.
    33  func newTestClient() *Client {
    34  	return &Client{
    35  		Key: testKeyEC,
    36  		dir: &Directory{}, // skip discovery
    37  	}
    38  }
    39  
    40  // Decodes a JWS-encoded request and unmarshals the decoded JSON into a provided
    41  // interface.
    42  func decodeJWSRequest(t *testing.T, v interface{}, r io.Reader) {
    43  	// Decode request
    44  	var req struct{ Payload string }
    45  	if err := json.NewDecoder(r).Decode(&req); err != nil {
    46  		t.Fatal(err)
    47  	}
    48  	payload, err := base64.RawURLEncoding.DecodeString(req.Payload)
    49  	if err != nil {
    50  		t.Fatal(err)
    51  	}
    52  	err = json.Unmarshal(payload, v)
    53  	if err != nil {
    54  		t.Fatal(err)
    55  	}
    56  }
    57  
    58  type jwsHead struct {
    59  	Alg   string
    60  	Nonce string
    61  	URL   string            `json:"url"`
    62  	KID   string            `json:"kid"`
    63  	JWK   map[string]string `json:"jwk"`
    64  }
    65  
    66  func decodeJWSHead(r io.Reader) (*jwsHead, error) {
    67  	var req struct{ Protected string }
    68  	if err := json.NewDecoder(r).Decode(&req); err != nil {
    69  		return nil, err
    70  	}
    71  	b, err := base64.RawURLEncoding.DecodeString(req.Protected)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	var head jwsHead
    76  	if err := json.Unmarshal(b, &head); err != nil {
    77  		return nil, err
    78  	}
    79  	return &head, nil
    80  }
    81  
    82  func TestRegisterWithoutKey(t *testing.T) {
    83  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    84  		if r.Method == "HEAD" {
    85  			w.Header().Set("Replay-Nonce", "test-nonce")
    86  			return
    87  		}
    88  		w.WriteHeader(http.StatusCreated)
    89  		fmt.Fprint(w, `{}`)
    90  	}))
    91  	defer ts.Close()
    92  	// First verify that using a complete client results in success.
    93  	c := Client{
    94  		Key:          testKeyEC,
    95  		DirectoryURL: ts.URL,
    96  		dir:          &Directory{RegURL: ts.URL},
    97  	}
    98  	if _, err := c.Register(context.Background(), &Account{}, AcceptTOS); err != nil {
    99  		t.Fatalf("c.Register() = %v; want success with a complete test client", err)
   100  	}
   101  	c.Key = nil
   102  	if _, err := c.Register(context.Background(), &Account{}, AcceptTOS); err == nil {
   103  		t.Error("c.Register() from client without key succeeded, wanted error")
   104  	}
   105  }
   106  
   107  func TestAuthorize(t *testing.T) {
   108  	tt := []struct{ typ, value string }{
   109  		{"dns", "example.com"},
   110  		{"ip", "1.2.3.4"},
   111  	}
   112  	for _, test := range tt {
   113  		t.Run(test.typ, func(t *testing.T) {
   114  			ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   115  				if r.Method == "HEAD" {
   116  					w.Header().Set("Replay-Nonce", "test-nonce")
   117  					return
   118  				}
   119  				if r.Method != "POST" {
   120  					t.Errorf("r.Method = %q; want POST", r.Method)
   121  				}
   122  
   123  				var j struct {
   124  					Resource   string
   125  					Identifier struct {
   126  						Type  string
   127  						Value string
   128  					}
   129  				}
   130  				decodeJWSRequest(t, &j, r.Body)
   131  
   132  				// Test request
   133  				if j.Resource != "new-authz" {
   134  					t.Errorf("j.Resource = %q; want new-authz", j.Resource)
   135  				}
   136  				if j.Identifier.Type != test.typ {
   137  					t.Errorf("j.Identifier.Type = %q; want %q", j.Identifier.Type, test.typ)
   138  				}
   139  				if j.Identifier.Value != test.value {
   140  					t.Errorf("j.Identifier.Value = %q; want %q", j.Identifier.Value, test.value)
   141  				}
   142  
   143  				w.Header().Set("Location", "https://ca.tld/acme/auth/1")
   144  				w.WriteHeader(http.StatusCreated)
   145  				fmt.Fprintf(w, `{
   146  					"identifier": {"type":%q,"value":%q},
   147  					"status":"pending",
   148  					"challenges":[
   149  						{
   150  							"type":"http-01",
   151  							"status":"pending",
   152  							"uri":"https://ca.tld/acme/challenge/publickey/id1",
   153  							"token":"token1"
   154  						},
   155  						{
   156  							"type":"tls-sni-01",
   157  							"status":"pending",
   158  							"uri":"https://ca.tld/acme/challenge/publickey/id2",
   159  							"token":"token2"
   160  						}
   161  					],
   162  					"combinations":[[0],[1]]
   163  				}`, test.typ, test.value)
   164  			}))
   165  			defer ts.Close()
   166  
   167  			var (
   168  				auth *Authorization
   169  				err  error
   170  			)
   171  			cl := Client{
   172  				Key:          testKeyEC,
   173  				DirectoryURL: ts.URL,
   174  				dir:          &Directory{AuthzURL: ts.URL},
   175  			}
   176  			switch test.typ {
   177  			case "dns":
   178  				auth, err = cl.Authorize(context.Background(), test.value)
   179  			case "ip":
   180  				auth, err = cl.AuthorizeIP(context.Background(), test.value)
   181  			default:
   182  				t.Fatalf("unknown identifier type: %q", test.typ)
   183  			}
   184  			if err != nil {
   185  				t.Fatal(err)
   186  			}
   187  
   188  			if auth.URI != "https://ca.tld/acme/auth/1" {
   189  				t.Errorf("URI = %q; want https://ca.tld/acme/auth/1", auth.URI)
   190  			}
   191  			if auth.Status != "pending" {
   192  				t.Errorf("Status = %q; want pending", auth.Status)
   193  			}
   194  			if auth.Identifier.Type != test.typ {
   195  				t.Errorf("Identifier.Type = %q; want %q", auth.Identifier.Type, test.typ)
   196  			}
   197  			if auth.Identifier.Value != test.value {
   198  				t.Errorf("Identifier.Value = %q; want %q", auth.Identifier.Value, test.value)
   199  			}
   200  
   201  			if n := len(auth.Challenges); n != 2 {
   202  				t.Fatalf("len(auth.Challenges) = %d; want 2", n)
   203  			}
   204  
   205  			c := auth.Challenges[0]
   206  			if c.Type != "http-01" {
   207  				t.Errorf("c.Type = %q; want http-01", c.Type)
   208  			}
   209  			if c.URI != "https://ca.tld/acme/challenge/publickey/id1" {
   210  				t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", c.URI)
   211  			}
   212  			if c.Token != "token1" {
   213  				t.Errorf("c.Token = %q; want token1", c.Token)
   214  			}
   215  
   216  			c = auth.Challenges[1]
   217  			if c.Type != "tls-sni-01" {
   218  				t.Errorf("c.Type = %q; want tls-sni-01", c.Type)
   219  			}
   220  			if c.URI != "https://ca.tld/acme/challenge/publickey/id2" {
   221  				t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id2", c.URI)
   222  			}
   223  			if c.Token != "token2" {
   224  				t.Errorf("c.Token = %q; want token2", c.Token)
   225  			}
   226  
   227  			combs := [][]int{{0}, {1}}
   228  			if !reflect.DeepEqual(auth.Combinations, combs) {
   229  				t.Errorf("auth.Combinations: %+v\nwant: %+v\n", auth.Combinations, combs)
   230  			}
   231  
   232  		})
   233  	}
   234  }
   235  
   236  func TestAuthorizeValid(t *testing.T) {
   237  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   238  		if r.Method == "HEAD" {
   239  			w.Header().Set("Replay-Nonce", "nonce")
   240  			return
   241  		}
   242  		w.WriteHeader(http.StatusCreated)
   243  		w.Write([]byte(`{"status":"valid"}`))
   244  	}))
   245  	defer ts.Close()
   246  	client := Client{
   247  		Key:          testKey,
   248  		DirectoryURL: ts.URL,
   249  		dir:          &Directory{AuthzURL: ts.URL},
   250  	}
   251  	_, err := client.Authorize(context.Background(), "example.com")
   252  	if err != nil {
   253  		t.Errorf("err = %v", err)
   254  	}
   255  }
   256  
   257  func TestWaitAuthorization(t *testing.T) {
   258  	t.Run("wait loop", func(t *testing.T) {
   259  		var count int
   260  		authz, err := runWaitAuthorization(context.Background(), t, func(w http.ResponseWriter, r *http.Request) {
   261  			count++
   262  			w.Header().Set("Retry-After", "0")
   263  			if count > 1 {
   264  				fmt.Fprintf(w, `{"status":"valid"}`)
   265  				return
   266  			}
   267  			fmt.Fprintf(w, `{"status":"pending"}`)
   268  		})
   269  		if err != nil {
   270  			t.Fatalf("non-nil error: %v", err)
   271  		}
   272  		if authz == nil {
   273  			t.Fatal("authz is nil")
   274  		}
   275  	})
   276  	t.Run("invalid status", func(t *testing.T) {
   277  		_, err := runWaitAuthorization(context.Background(), t, func(w http.ResponseWriter, r *http.Request) {
   278  			fmt.Fprintf(w, `{"status":"invalid"}`)
   279  		})
   280  		if _, ok := err.(*AuthorizationError); !ok {
   281  			t.Errorf("err is %v (%T); want non-nil *AuthorizationError", err, err)
   282  		}
   283  	})
   284  	t.Run("invalid status with error returns the authorization error", func(t *testing.T) {
   285  		_, err := runWaitAuthorization(context.Background(), t, func(w http.ResponseWriter, r *http.Request) {
   286  			fmt.Fprintf(w, `{
   287  				"type": "dns-01",
   288  				"status": "invalid",
   289  				"error": {
   290  				  "type": "urn:ietf:params:acme:error:caa",
   291  				  "detail": "CAA record for <domain> prevents issuance",
   292  				  "status": 403
   293  				},
   294  				"url": "https://acme-v02.api.letsencrypt.org/acme/chall-v3/xxx/xxx",
   295  				"token": "xxx",
   296  				"validationRecord": [
   297  				  {
   298  					"hostname": "<domain>"
   299  				  }
   300  				]
   301  			  }`)
   302  		})
   303  
   304  		want := &AuthorizationError{
   305  			Errors: []error{
   306  				(&wireError{
   307  					Status: 403,
   308  					Type:   "urn:ietf:params:acme:error:caa",
   309  					Detail: "CAA record for <domain> prevents issuance",
   310  				}).error(nil),
   311  			},
   312  		}
   313  
   314  		_, ok := err.(*AuthorizationError)
   315  		if !ok {
   316  			t.Errorf("err is %T; want non-nil *AuthorizationError", err)
   317  		}
   318  
   319  		if err.Error() != want.Error() {
   320  			t.Errorf("err is %v; want %v", err, want)
   321  		}
   322  	})
   323  	t.Run("non-retriable error", func(t *testing.T) {
   324  		const code = http.StatusBadRequest
   325  		_, err := runWaitAuthorization(context.Background(), t, func(w http.ResponseWriter, r *http.Request) {
   326  			w.WriteHeader(code)
   327  		})
   328  		res, ok := err.(*Error)
   329  		if !ok {
   330  			t.Fatalf("err is %v (%T); want a non-nil *Error", err, err)
   331  		}
   332  		if res.StatusCode != code {
   333  			t.Errorf("res.StatusCode = %d; want %d", res.StatusCode, code)
   334  		}
   335  	})
   336  	for _, code := range []int{http.StatusTooManyRequests, http.StatusInternalServerError} {
   337  		t.Run(fmt.Sprintf("retriable %d error", code), func(t *testing.T) {
   338  			var count int
   339  			authz, err := runWaitAuthorization(context.Background(), t, func(w http.ResponseWriter, r *http.Request) {
   340  				count++
   341  				w.Header().Set("Retry-After", "0")
   342  				if count > 1 {
   343  					fmt.Fprintf(w, `{"status":"valid"}`)
   344  					return
   345  				}
   346  				w.WriteHeader(code)
   347  			})
   348  			if err != nil {
   349  				t.Fatalf("non-nil error: %v", err)
   350  			}
   351  			if authz == nil {
   352  				t.Fatal("authz is nil")
   353  			}
   354  		})
   355  	}
   356  	t.Run("context cancel", func(t *testing.T) {
   357  		ctx, cancel := context.WithCancel(context.Background())
   358  		defer cancel()
   359  		_, err := runWaitAuthorization(ctx, t, func(w http.ResponseWriter, r *http.Request) {
   360  			w.Header().Set("Retry-After", "60")
   361  			fmt.Fprintf(w, `{"status":"pending"}`)
   362  			time.AfterFunc(1*time.Millisecond, cancel)
   363  		})
   364  		if err == nil {
   365  			t.Error("err is nil")
   366  		}
   367  	})
   368  }
   369  
   370  func runWaitAuthorization(ctx context.Context, t *testing.T, h http.HandlerFunc) (*Authorization, error) {
   371  	t.Helper()
   372  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   373  		w.Header().Set("Replay-Nonce", fmt.Sprintf("bad-test-nonce-%v", time.Now().UnixNano()))
   374  		h(w, r)
   375  	}))
   376  	defer ts.Close()
   377  
   378  	client := &Client{
   379  		Key:          testKey,
   380  		DirectoryURL: ts.URL,
   381  		dir:          &Directory{},
   382  		KID:          "some-key-id", // set to avoid lookup attempt
   383  	}
   384  	return client.WaitAuthorization(ctx, ts.URL)
   385  }
   386  
   387  func TestRevokeAuthorization(t *testing.T) {
   388  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   389  		if r.Method == "HEAD" {
   390  			w.Header().Set("Replay-Nonce", "nonce")
   391  			return
   392  		}
   393  		switch r.URL.Path {
   394  		case "/1":
   395  			var req struct {
   396  				Resource string
   397  				Status   string
   398  				Delete   bool
   399  			}
   400  			decodeJWSRequest(t, &req, r.Body)
   401  			if req.Resource != "authz" {
   402  				t.Errorf("req.Resource = %q; want authz", req.Resource)
   403  			}
   404  			if req.Status != "deactivated" {
   405  				t.Errorf("req.Status = %q; want deactivated", req.Status)
   406  			}
   407  			if !req.Delete {
   408  				t.Errorf("req.Delete is false")
   409  			}
   410  		case "/2":
   411  			w.WriteHeader(http.StatusBadRequest)
   412  		}
   413  	}))
   414  	defer ts.Close()
   415  	client := &Client{
   416  		Key:          testKey,
   417  		DirectoryURL: ts.URL,       // don't dial outside of localhost
   418  		dir:          &Directory{}, // don't do discovery
   419  	}
   420  	ctx := context.Background()
   421  	if err := client.RevokeAuthorization(ctx, ts.URL+"/1"); err != nil {
   422  		t.Errorf("err = %v", err)
   423  	}
   424  	if client.RevokeAuthorization(ctx, ts.URL+"/2") == nil {
   425  		t.Error("nil error")
   426  	}
   427  }
   428  
   429  func TestFetchCertCancel(t *testing.T) {
   430  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   431  		<-r.Context().Done()
   432  		w.Header().Set("Retry-After", "0")
   433  		w.WriteHeader(http.StatusBadRequest)
   434  	}))
   435  	defer ts.Close()
   436  	ctx, cancel := context.WithCancel(context.Background())
   437  	done := make(chan struct{})
   438  	var err error
   439  	go func() {
   440  		cl := newTestClient()
   441  		_, err = cl.FetchCert(ctx, ts.URL, false)
   442  		close(done)
   443  	}()
   444  	cancel()
   445  	<-done
   446  	if err != context.Canceled {
   447  		t.Errorf("err = %v; want %v", err, context.Canceled)
   448  	}
   449  }
   450  
   451  func TestFetchCertDepth(t *testing.T) {
   452  	var count byte
   453  	var ts *httptest.Server
   454  	ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   455  		count++
   456  		if count > maxChainLen+1 {
   457  			t.Errorf("count = %d; want at most %d", count, maxChainLen+1)
   458  			w.WriteHeader(http.StatusInternalServerError)
   459  		}
   460  		w.Header().Set("Link", fmt.Sprintf("<%s>;rel=up", ts.URL))
   461  		w.Write([]byte{count})
   462  	}))
   463  	defer ts.Close()
   464  	cl := newTestClient()
   465  	_, err := cl.FetchCert(context.Background(), ts.URL, true)
   466  	if err == nil {
   467  		t.Errorf("err is nil")
   468  	}
   469  }
   470  
   471  func TestFetchCertBreadth(t *testing.T) {
   472  	var ts *httptest.Server
   473  	ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   474  		for i := 0; i < maxChainLen+1; i++ {
   475  			w.Header().Add("Link", fmt.Sprintf("<%s>;rel=up", ts.URL))
   476  		}
   477  		w.Write([]byte{1})
   478  	}))
   479  	defer ts.Close()
   480  	cl := newTestClient()
   481  	_, err := cl.FetchCert(context.Background(), ts.URL, true)
   482  	if err == nil {
   483  		t.Errorf("err is nil")
   484  	}
   485  }
   486  
   487  func TestFetchCertSize(t *testing.T) {
   488  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   489  		b := bytes.Repeat([]byte{1}, maxCertSize+1)
   490  		w.Write(b)
   491  	}))
   492  	defer ts.Close()
   493  	cl := newTestClient()
   494  	_, err := cl.FetchCert(context.Background(), ts.URL, false)
   495  	if err == nil {
   496  		t.Errorf("err is nil")
   497  	}
   498  }
   499  
   500  func TestNonce_add(t *testing.T) {
   501  	var c Client
   502  	c.addNonce(http.Header{"Replay-Nonce": {"nonce"}})
   503  	c.addNonce(http.Header{"Replay-Nonce": {}})
   504  	c.addNonce(http.Header{"Replay-Nonce": {"nonce"}})
   505  
   506  	nonces := map[string]struct{}{"nonce": {}}
   507  	if !reflect.DeepEqual(c.nonces, nonces) {
   508  		t.Errorf("c.nonces = %q; want %q", c.nonces, nonces)
   509  	}
   510  }
   511  
   512  func TestNonce_addMax(t *testing.T) {
   513  	c := &Client{nonces: make(map[string]struct{})}
   514  	for i := 0; i < maxNonces; i++ {
   515  		c.nonces[fmt.Sprintf("%d", i)] = struct{}{}
   516  	}
   517  	c.addNonce(http.Header{"Replay-Nonce": {"nonce"}})
   518  	if n := len(c.nonces); n != maxNonces {
   519  		t.Errorf("len(c.nonces) = %d; want %d", n, maxNonces)
   520  	}
   521  }
   522  
   523  func TestNonce_fetch(t *testing.T) {
   524  	tests := []struct {
   525  		code  int
   526  		nonce string
   527  	}{
   528  		{http.StatusOK, "nonce1"},
   529  		{http.StatusBadRequest, "nonce2"},
   530  		{http.StatusOK, ""},
   531  	}
   532  	var i int
   533  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   534  		if r.Method != "HEAD" {
   535  			t.Errorf("%d: r.Method = %q; want HEAD", i, r.Method)
   536  		}
   537  		w.Header().Set("Replay-Nonce", tests[i].nonce)
   538  		w.WriteHeader(tests[i].code)
   539  	}))
   540  	defer ts.Close()
   541  	for ; i < len(tests); i++ {
   542  		test := tests[i]
   543  		c := newTestClient()
   544  		n, err := c.fetchNonce(context.Background(), ts.URL)
   545  		if n != test.nonce {
   546  			t.Errorf("%d: n=%q; want %q", i, n, test.nonce)
   547  		}
   548  		switch {
   549  		case err == nil && test.nonce == "":
   550  			t.Errorf("%d: n=%q, err=%v; want non-nil error", i, n, err)
   551  		case err != nil && test.nonce != "":
   552  			t.Errorf("%d: n=%q, err=%v; want %q", i, n, err, test.nonce)
   553  		}
   554  	}
   555  }
   556  
   557  func TestNonce_fetchError(t *testing.T) {
   558  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   559  		w.WriteHeader(http.StatusTooManyRequests)
   560  	}))
   561  	defer ts.Close()
   562  	c := newTestClient()
   563  	_, err := c.fetchNonce(context.Background(), ts.URL)
   564  	e, ok := err.(*Error)
   565  	if !ok {
   566  		t.Fatalf("err is %T; want *Error", err)
   567  	}
   568  	if e.StatusCode != http.StatusTooManyRequests {
   569  		t.Errorf("e.StatusCode = %d; want %d", e.StatusCode, http.StatusTooManyRequests)
   570  	}
   571  }
   572  
   573  func TestNonce_popWhenEmpty(t *testing.T) {
   574  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   575  		if r.Method != "HEAD" {
   576  			t.Errorf("r.Method = %q; want HEAD", r.Method)
   577  		}
   578  		switch r.URL.Path {
   579  		case "/dir-with-nonce":
   580  			w.Header().Set("Replay-Nonce", "dirnonce")
   581  		case "/new-nonce":
   582  			w.Header().Set("Replay-Nonce", "newnonce")
   583  		case "/dir-no-nonce", "/empty":
   584  			// No nonce in the header.
   585  		default:
   586  			t.Errorf("Unknown URL: %s", r.URL)
   587  		}
   588  	}))
   589  	defer ts.Close()
   590  	ctx := context.Background()
   591  
   592  	tt := []struct {
   593  		dirURL, popURL, nonce string
   594  		wantOK                bool
   595  	}{
   596  		{ts.URL + "/dir-with-nonce", ts.URL + "/new-nonce", "dirnonce", true},
   597  		{ts.URL + "/dir-no-nonce", ts.URL + "/new-nonce", "newnonce", true},
   598  		{ts.URL + "/dir-no-nonce", ts.URL + "/empty", "", false},
   599  	}
   600  	for _, test := range tt {
   601  		t.Run(fmt.Sprintf("nonce:%s wantOK:%v", test.nonce, test.wantOK), func(t *testing.T) {
   602  			c := Client{DirectoryURL: test.dirURL}
   603  			v, err := c.popNonce(ctx, test.popURL)
   604  			if !test.wantOK {
   605  				if err == nil {
   606  					t.Fatalf("c.popNonce(%q) returned nil error", test.popURL)
   607  				}
   608  				return
   609  			}
   610  			if err != nil {
   611  				t.Fatalf("c.popNonce(%q): %v", test.popURL, err)
   612  			}
   613  			if v != test.nonce {
   614  				t.Errorf("c.popNonce(%q) = %q; want %q", test.popURL, v, test.nonce)
   615  			}
   616  		})
   617  	}
   618  }
   619  
   620  func TestLinkHeader(t *testing.T) {
   621  	h := http.Header{"Link": {
   622  		`<https://example.com/acme/new-authz>;rel="next"`,
   623  		`<https://example.com/acme/recover-reg>; rel=recover`,
   624  		`<https://example.com/acme/terms>; foo=bar; rel="terms-of-service"`,
   625  		`<dup>;rel="next"`,
   626  	}}
   627  	tests := []struct {
   628  		rel string
   629  		out []string
   630  	}{
   631  		{"next", []string{"https://example.com/acme/new-authz", "dup"}},
   632  		{"recover", []string{"https://example.com/acme/recover-reg"}},
   633  		{"terms-of-service", []string{"https://example.com/acme/terms"}},
   634  		{"empty", nil},
   635  	}
   636  	for i, test := range tests {
   637  		if v := linkHeader(h, test.rel); !reflect.DeepEqual(v, test.out) {
   638  			t.Errorf("%d: linkHeader(%q): %v; want %v", i, test.rel, v, test.out)
   639  		}
   640  	}
   641  }
   642  
   643  func TestTLSSNI01ChallengeCert(t *testing.T) {
   644  	const (
   645  		token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
   646  		// echo -n <token.testKeyECThumbprint> | shasum -a 256
   647  		san = "dbbd5eefe7b4d06eb9d1d9f5acb4c7cd.a27d320e4b30332f0b6cb441734ad7b0.acme.invalid"
   648  	)
   649  
   650  	tlscert, name, err := newTestClient().TLSSNI01ChallengeCert(token)
   651  	if err != nil {
   652  		t.Fatal(err)
   653  	}
   654  
   655  	if n := len(tlscert.Certificate); n != 1 {
   656  		t.Fatalf("len(tlscert.Certificate) = %d; want 1", n)
   657  	}
   658  	cert, err := x509.ParseCertificate(tlscert.Certificate[0])
   659  	if err != nil {
   660  		t.Fatal(err)
   661  	}
   662  	if len(cert.DNSNames) != 1 || cert.DNSNames[0] != san {
   663  		t.Fatalf("cert.DNSNames = %v; want %q", cert.DNSNames, san)
   664  	}
   665  	if cert.DNSNames[0] != name {
   666  		t.Errorf("cert.DNSNames[0] != name: %q vs %q", cert.DNSNames[0], name)
   667  	}
   668  	if cn := cert.Subject.CommonName; cn != san {
   669  		t.Errorf("cert.Subject.CommonName = %q; want %q", cn, san)
   670  	}
   671  }
   672  
   673  func TestTLSSNI02ChallengeCert(t *testing.T) {
   674  	const (
   675  		token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
   676  		// echo -n evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA | shasum -a 256
   677  		sanA = "7ea0aaa69214e71e02cebb18bb867736.09b730209baabf60e43d4999979ff139.token.acme.invalid"
   678  		// echo -n <token.testKeyECThumbprint> | shasum -a 256
   679  		sanB = "dbbd5eefe7b4d06eb9d1d9f5acb4c7cd.a27d320e4b30332f0b6cb441734ad7b0.ka.acme.invalid"
   680  	)
   681  
   682  	tlscert, name, err := newTestClient().TLSSNI02ChallengeCert(token)
   683  	if err != nil {
   684  		t.Fatal(err)
   685  	}
   686  
   687  	if n := len(tlscert.Certificate); n != 1 {
   688  		t.Fatalf("len(tlscert.Certificate) = %d; want 1", n)
   689  	}
   690  	cert, err := x509.ParseCertificate(tlscert.Certificate[0])
   691  	if err != nil {
   692  		t.Fatal(err)
   693  	}
   694  	names := []string{sanA, sanB}
   695  	if !reflect.DeepEqual(cert.DNSNames, names) {
   696  		t.Fatalf("cert.DNSNames = %v;\nwant %v", cert.DNSNames, names)
   697  	}
   698  	sort.Strings(cert.DNSNames)
   699  	i := sort.SearchStrings(cert.DNSNames, name)
   700  	if i >= len(cert.DNSNames) || cert.DNSNames[i] != name {
   701  		t.Errorf("%v doesn't have %q", cert.DNSNames, name)
   702  	}
   703  	if cn := cert.Subject.CommonName; cn != sanA {
   704  		t.Errorf("CommonName = %q; want %q", cn, sanA)
   705  	}
   706  }
   707  
   708  func TestTLSALPN01ChallengeCert(t *testing.T) {
   709  	const (
   710  		token   = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
   711  		keyAuth = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA." + testKeyECThumbprint
   712  		// echo -n <token.testKeyECThumbprint> | shasum -a 256
   713  		h      = "0420dbbd5eefe7b4d06eb9d1d9f5acb4c7cda27d320e4b30332f0b6cb441734ad7b0"
   714  		domain = "example.com"
   715  	)
   716  
   717  	extValue, err := hex.DecodeString(h)
   718  	if err != nil {
   719  		t.Fatal(err)
   720  	}
   721  
   722  	tlscert, err := newTestClient().TLSALPN01ChallengeCert(token, domain)
   723  	if err != nil {
   724  		t.Fatal(err)
   725  	}
   726  
   727  	if n := len(tlscert.Certificate); n != 1 {
   728  		t.Fatalf("len(tlscert.Certificate) = %d; want 1", n)
   729  	}
   730  	cert, err := x509.ParseCertificate(tlscert.Certificate[0])
   731  	if err != nil {
   732  		t.Fatal(err)
   733  	}
   734  	names := []string{domain}
   735  	if !reflect.DeepEqual(cert.DNSNames, names) {
   736  		t.Fatalf("cert.DNSNames = %v;\nwant %v", cert.DNSNames, names)
   737  	}
   738  	if cn := cert.Subject.CommonName; cn != domain {
   739  		t.Errorf("CommonName = %q; want %q", cn, domain)
   740  	}
   741  	acmeExts := []pkix.Extension{}
   742  	for _, ext := range cert.Extensions {
   743  		if idPeACMEIdentifier.Equal(ext.Id) {
   744  			acmeExts = append(acmeExts, ext)
   745  		}
   746  	}
   747  	if len(acmeExts) != 1 {
   748  		t.Errorf("acmeExts = %v; want exactly one", acmeExts)
   749  	}
   750  	if !acmeExts[0].Critical {
   751  		t.Errorf("acmeExt.Critical = %v; want true", acmeExts[0].Critical)
   752  	}
   753  	if bytes.Compare(acmeExts[0].Value, extValue) != 0 {
   754  		t.Errorf("acmeExt.Value = %v; want %v", acmeExts[0].Value, extValue)
   755  	}
   756  
   757  }
   758  
   759  func TestTLSChallengeCertOpt(t *testing.T) {
   760  	key, err := rsa.GenerateKey(rand.Reader, 512)
   761  	if err != nil {
   762  		t.Fatal(err)
   763  	}
   764  	tmpl := &x509.Certificate{
   765  		SerialNumber: big.NewInt(2),
   766  		Subject:      pkix.Name{Organization: []string{"Test"}},
   767  		DNSNames:     []string{"should-be-overwritten"},
   768  	}
   769  	opts := []CertOption{WithKey(key), WithTemplate(tmpl)}
   770  
   771  	client := newTestClient()
   772  	cert1, _, err := client.TLSSNI01ChallengeCert("token", opts...)
   773  	if err != nil {
   774  		t.Fatal(err)
   775  	}
   776  	cert2, _, err := client.TLSSNI02ChallengeCert("token", opts...)
   777  	if err != nil {
   778  		t.Fatal(err)
   779  	}
   780  
   781  	for i, tlscert := range []tls.Certificate{cert1, cert2} {
   782  		// verify generated cert private key
   783  		tlskey, ok := tlscert.PrivateKey.(*rsa.PrivateKey)
   784  		if !ok {
   785  			t.Errorf("%d: tlscert.PrivateKey is %T; want *rsa.PrivateKey", i, tlscert.PrivateKey)
   786  			continue
   787  		}
   788  		if tlskey.D.Cmp(key.D) != 0 {
   789  			t.Errorf("%d: tlskey.D = %v; want %v", i, tlskey.D, key.D)
   790  		}
   791  		// verify generated cert public key
   792  		x509Cert, err := x509.ParseCertificate(tlscert.Certificate[0])
   793  		if err != nil {
   794  			t.Errorf("%d: %v", i, err)
   795  			continue
   796  		}
   797  		tlspub, ok := x509Cert.PublicKey.(*rsa.PublicKey)
   798  		if !ok {
   799  			t.Errorf("%d: x509Cert.PublicKey is %T; want *rsa.PublicKey", i, x509Cert.PublicKey)
   800  			continue
   801  		}
   802  		if tlspub.N.Cmp(key.N) != 0 {
   803  			t.Errorf("%d: tlspub.N = %v; want %v", i, tlspub.N, key.N)
   804  		}
   805  		// verify template option
   806  		sn := big.NewInt(2)
   807  		if x509Cert.SerialNumber.Cmp(sn) != 0 {
   808  			t.Errorf("%d: SerialNumber = %v; want %v", i, x509Cert.SerialNumber, sn)
   809  		}
   810  		org := []string{"Test"}
   811  		if !reflect.DeepEqual(x509Cert.Subject.Organization, org) {
   812  			t.Errorf("%d: Subject.Organization = %+v; want %+v", i, x509Cert.Subject.Organization, org)
   813  		}
   814  		for _, v := range x509Cert.DNSNames {
   815  			if !strings.HasSuffix(v, ".acme.invalid") {
   816  				t.Errorf("%d: invalid DNSNames element: %q", i, v)
   817  			}
   818  		}
   819  	}
   820  }
   821  
   822  func TestHTTP01Challenge(t *testing.T) {
   823  	const (
   824  		token = "xxx"
   825  		// thumbprint is precomputed for testKeyEC in jws_test.go
   826  		value   = token + "." + testKeyECThumbprint
   827  		urlpath = "/.well-known/acme-challenge/" + token
   828  	)
   829  	client := newTestClient()
   830  	val, err := client.HTTP01ChallengeResponse(token)
   831  	if err != nil {
   832  		t.Fatal(err)
   833  	}
   834  	if val != value {
   835  		t.Errorf("val = %q; want %q", val, value)
   836  	}
   837  	if path := client.HTTP01ChallengePath(token); path != urlpath {
   838  		t.Errorf("path = %q; want %q", path, urlpath)
   839  	}
   840  }
   841  
   842  func TestDNS01ChallengeRecord(t *testing.T) {
   843  	// echo -n xxx.<testKeyECThumbprint> | \
   844  	//      openssl dgst -binary -sha256 | \
   845  	//      base64 | tr -d '=' | tr '/+' '_-'
   846  	const value = "8DERMexQ5VcdJ_prpPiA0mVdp7imgbCgjsG4SqqNMIo"
   847  
   848  	val, err := newTestClient().DNS01ChallengeRecord("xxx")
   849  	if err != nil {
   850  		t.Fatal(err)
   851  	}
   852  	if val != value {
   853  		t.Errorf("val = %q; want %q", val, value)
   854  	}
   855  }
   856  

View as plain text