From 3f311e442e372a8282abb4d82b6d59ac79a97f75 Mon Sep 17 00:00:00 2001 From: Ian Stapleton Cordasco Date: Sat, 29 Mar 2025 20:55:24 -0500 Subject: acme: return error from pre-authorization when unsupported Check the directory's AuthzURL to see if the server supports pre-authorization. If it's empty, then the server is not advertising support and we can encounter other bugs. Better to return early and give a clear error to the caller. From https://www.rfc-editor.org/rfc/rfc8555#section-7.4.1 If a CA wishes to allow pre-authorization within ACME, it can offer a "new authorization" resource in its directory by adding the field "newAuthz" with a URL for the newAuthz resource. Fixes golang/go#40839 Change-Id: Id3e92e8e2ae3c57285183d37544dd59b4988b3be Reviewed-on: https://go-review.googlesource.com/c/crypto/+/661675 Reviewed-by: Dmitri Shuralyov Auto-Submit: Dmitri Shuralyov Reviewed-by: Roland Shoemaker LUCI-TryBot-Result: Go LUCI Reviewed-by: Dmitri Shuralyov --- acme/acme.go | 4 ++++ acme/acme_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ acme/types.go | 4 ++++ 3 files changed, 60 insertions(+) diff --git a/acme/acme.go b/acme/acme.go index a43c62f..cfb1dfd 100644 --- a/acme/acme.go +++ b/acme/acme.go @@ -353,6 +353,10 @@ func (c *Client) authorize(ctx context.Context, typ, val string) (*Authorization if _, err := c.Discover(ctx); err != nil { return nil, err } + if c.dir.AuthzURL == "" { + // Pre-Authorization is unsupported + return nil, errPreAuthorizationNotSupported + } type authzID struct { Type string `json:"type"` diff --git a/acme/acme_test.go b/acme/acme_test.go index a090670..d286888 100644 --- a/acme/acme_test.go +++ b/acme/acme_test.go @@ -15,6 +15,7 @@ import ( "encoding/base64" "encoding/hex" "encoding/json" + "errors" "fmt" "io" "math/big" @@ -254,6 +255,57 @@ func TestAuthorizeValid(t *testing.T) { } } +func TestAuthorizeUnsupported(t *testing.T) { + const ( + nonce = "https://example.com/acme/new-nonce" + reg = "https://example.com/acme/new-acct" + order = "https://example.com/acme/new-order" + revoke = "https://example.com/acme/revoke-cert" + keychange = "https://example.com/acme/key-change" + metaTerms = "https://example.com/acme/terms/2017-5-30" + metaWebsite = "https://www.example.com/" + metaCAA = "example.com" + ) + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Replay-Nonce", "nonce") + if r.Method == http.MethodHead { + return + } + switch r.URL.Path { + case "/": // Directory + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `{ + "newNonce": %q, + "newAccount": %q, + "newOrder": %q, + "revokeCert": %q, + "keyChange": %q, + "meta": { + "termsOfService": %q, + "website": %q, + "caaIdentities": [%q], + "externalAccountRequired": true + } + }`, nonce, reg, order, revoke, keychange, metaTerms, metaWebsite, metaCAA) + w.WriteHeader(http.StatusOK) + case "/acme/new-authz": + w.WriteHeader(http.StatusBadRequest) + } + })) + defer ts.Close() + client := &Client{Key: testKey, DirectoryURL: ts.URL} + dir, err := client.Discover(context.Background()) + if err != nil { + t.Fatal(err) + } + if dir.AuthzURL != "" { + t.Fatalf("expected AuthzURL to be empty, got %q", dir.AuthzURL) + } + if _, err := client.Authorize(context.Background(), "example.com"); !errors.Is(err, errPreAuthorizationNotSupported) { + t.Errorf("expected err to indicate pre-authorization is unsupported, got %+v", err) + } +} + func TestWaitAuthorization(t *testing.T) { t.Run("wait loop", func(t *testing.T) { var count int diff --git a/acme/types.go b/acme/types.go index 45492ad..640223c 100644 --- a/acme/types.go +++ b/acme/types.go @@ -56,6 +56,10 @@ var ( // ErrNoAccount indicates that the Client's key has not been registered with the CA. ErrNoAccount = errors.New("acme: account does not exist") + + // errPreAuthorizationNotSupported indicates that the server does not + // support pre-authorization of identifiers. + errPreAuthorizationNotSupported = errors.New("acme: pre-authorization is not supported") ) // A Subproblem describes an ACME subproblem as reported in an Error. -- cgit v1.3