فهرست منبع

acme: fetch fresh nonces from newNonce resource

Previously, nonce values were fetched from Directory URL.
RFC8555 and some recent drafts provide specific URL to fetch
new nonce values from.

This CL makes the client always use new nonce URL when available
and fall back to the previous behavior otherwise.

Updates golang/go#21081

Change-Id: I6442004b01d46aa015c193ca4c80daa712b78790
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/191603
Run-TryBot: Alex Vaghin <ddos@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Alex Vaghin 6 سال پیش
والد
کامیت
2682ddc9f5
2فایلهای تغییر یافته به همراه43 افزوده شده و 2 حذف شده
  1. 6 2
      acme/acme.go
  2. 37 0
      acme/rfc8555_test.go

+ 6 - 2
acme/acme.go

@@ -750,12 +750,16 @@ func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Accoun
 }
 
 // popNonce returns a nonce value previously stored with c.addNonce
-// or fetches a fresh one from a URL by issuing a HEAD request.
-// It first tries c.directoryURL() and then the provided url if the former fails.
+// or fetches a fresh one from c.dir.NonceURL.
+// If NonceURL is empty, it first tries c.directoryURL() and, failing that,
+// the provided url.
 func (c *Client) popNonce(ctx context.Context, url string) (string, error) {
 	c.noncesMu.Lock()
 	defer c.noncesMu.Unlock()
 	if len(c.nonces) == 0 {
+		if c.dir != nil && c.dir.NonceURL != "" {
+			return c.fetchNonce(ctx, c.dir.NonceURL)
+		}
 		dirURL := c.directoryURL()
 		v, err := c.fetchNonce(ctx, dirURL)
 		if err != nil && url != dirURL {

+ 37 - 0
acme/rfc8555_test.go

@@ -84,3 +84,40 @@ func TestRFC_Discover(t *testing.T) {
 		t.Error("dir.Meta.ExternalAccountRequired is false")
 	}
 }
+
+func TestRFC_popNonce(t *testing.T) {
+	var count int
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		// The Client uses only Directory.NonceURL when specified.
+		// Expect no other URL paths.
+		if r.URL.Path != "/new-nonce" {
+			t.Errorf("r.URL.Path = %q; want /new-nonce", r.URL.Path)
+		}
+		if count > 0 {
+			w.WriteHeader(http.StatusTooManyRequests)
+			return
+		}
+		count++
+		w.Header().Set("Replay-Nonce", "second")
+	}))
+	cl := &Client{
+		DirectoryURL: ts.URL,
+		dir:          &Directory{NonceURL: ts.URL + "/new-nonce"},
+	}
+	cl.addNonce(http.Header{"Replay-Nonce": {"first"}})
+
+	for i, nonce := range []string{"first", "second"} {
+		v, err := cl.popNonce(context.Background(), "")
+		if err != nil {
+			t.Errorf("%d: cl.popNonce: %v", i, err)
+		}
+		if v != nonce {
+			t.Errorf("%d: cl.popNonce = %q; want %q", i, v, nonce)
+		}
+	}
+	// No more nonces and server replies with an error past first nonce fetch.
+	// Expected to fail.
+	if _, err := cl.popNonce(context.Background(), ""); err == nil {
+		t.Error("last cl.popNonce returned nil error")
+	}
+}