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 <dmitshur@google.com>
Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
This commit is contained in:
Ian Stapleton Cordasco
2025-04-17 11:16:57 -07:00
committed by Gopher Robot
parent 1f7c62cd66
commit 3f311e442e
3 changed files with 60 additions and 0 deletions
+4
View File
@@ -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"`
+52
View File
@@ -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
+4
View File
@@ -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.