ソースを参照

tempfork/acme: update to latest version (#15543)

Pull in https://github.com/tailscale/golang-x-crypto/pull/16

Updates #15542

Signed-off-by: Andrew Lytvynov <[email protected]>
Andrew Lytvynov 11 ヶ月 前
コミット
46505ca338
7 ファイル変更52 行追加13 行削除
  1. 1 1
      go.mod
  2. 2 2
      go.sum
  3. 7 6
      tempfork/acme/acme.go
  4. 1 4
      tempfork/acme/acme_test.go
  5. 10 0
      tempfork/acme/rfc8555.go
  6. 7 0
      tempfork/acme/rfc8555_test.go
  7. 24 0
      tempfork/acme/types.go

+ 1 - 1
go.mod

@@ -77,7 +77,7 @@ require (
 	github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e
 	github.com/tailscale/depaware v0.0.0-20250112153213-b748de04d81b
 	github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41
-	github.com/tailscale/golang-x-crypto v0.0.0-20250218230618-9a281fd8faca
+	github.com/tailscale/golang-x-crypto v0.0.0-20250404221719-a5573b049869
 	github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05
 	github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a
 	github.com/tailscale/mkctr v0.0.0-20250228050937-c75ea1476830

+ 2 - 2
go.sum

@@ -910,8 +910,8 @@ github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8
 github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg=
 github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41 h1:/V2rCMMWcsjYaYO2MeovLw+ClP63OtXgCF2Y1eb8+Ns=
 github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41/go.mod h1:/roCdA6gg6lQyw/Oz6gIIGu3ggJKYhF+WC/AQReE5XQ=
-github.com/tailscale/golang-x-crypto v0.0.0-20250218230618-9a281fd8faca h1:ecjHwH73Yvqf/oIdQ2vxAX+zc6caQsYdPzsxNW1J3G8=
-github.com/tailscale/golang-x-crypto v0.0.0-20250218230618-9a281fd8faca/go.mod h1:ikbF+YT089eInTp9f2vmvy4+ZVnW5hzX1q2WknxSprQ=
+github.com/tailscale/golang-x-crypto v0.0.0-20250404221719-a5573b049869 h1:SRL6irQkKGQKKLzvQP/ke/2ZuB7Py5+XuqtOgSj+iMM=
+github.com/tailscale/golang-x-crypto v0.0.0-20250404221719-a5573b049869/go.mod h1:ikbF+YT089eInTp9f2vmvy4+ZVnW5hzX1q2WknxSprQ=
 github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPxRs2O36yuGRW3f9SYV+bMTTvMBI0EKio=
 github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8=
 github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=

+ 7 - 6
tempfork/acme/acme.go

@@ -270,10 +270,7 @@ func (c *Client) FetchRenewalInfo(ctx context.Context, leaf []byte) (*RenewalInf
 		return nil, fmt.Errorf("parsing leaf certificate: %w", err)
 	}
 
-	renewalURL, err := c.getRenewalURL(parsedLeaf)
-	if err != nil {
-		return nil, fmt.Errorf("generating renewal info URL: %w", err)
-	}
+	renewalURL := c.getRenewalURL(parsedLeaf)
 
 	res, err := c.get(ctx, renewalURL, wantStatus(http.StatusOK))
 	if err != nil {
@@ -288,16 +285,20 @@ func (c *Client) FetchRenewalInfo(ctx context.Context, leaf []byte) (*RenewalInf
 	return &info, nil
 }
 
-func (c *Client) getRenewalURL(cert *x509.Certificate) (string, error) {
+func (c *Client) getRenewalURL(cert *x509.Certificate) string {
 	// See https://www.ietf.org/archive/id/draft-ietf-acme-ari-04.html#name-the-renewalinfo-resource
 	// for how the request URL is built.
 	url := c.dir.RenewalInfoURL
 	if !strings.HasSuffix(url, "/") {
 		url += "/"
 	}
+	return url + certRenewalIdentifier(cert)
+}
+
+func certRenewalIdentifier(cert *x509.Certificate) string {
 	aki := base64.RawURLEncoding.EncodeToString(cert.AuthorityKeyId)
 	serial := base64.RawURLEncoding.EncodeToString(cert.SerialNumber.Bytes())
-	return fmt.Sprintf("%s%s.%s", url, aki, serial), nil
+	return aki + "." + serial
 }
 
 // AcceptTOS always returns true to indicate the acceptance of a CA's Terms of Service

+ 1 - 4
tempfork/acme/acme_test.go

@@ -549,10 +549,7 @@ func TestGetRenewalURL(t *testing.T) {
 	}
 
 	client := newTestClientWithMockDirectory()
-	urlString, err := client.getRenewalURL(parsedLeaf)
-	if err != nil {
-		t.Fatal(err)
-	}
+	urlString := client.getRenewalURL(parsedLeaf)
 
 	parsedURL, err := url.Parse(urlString)
 	if err != nil {

+ 10 - 0
tempfork/acme/rfc8555.go

@@ -7,6 +7,7 @@ package acme
 import (
 	"context"
 	"crypto"
+	"crypto/x509"
 	"encoding/base64"
 	"encoding/json"
 	"encoding/pem"
@@ -205,6 +206,7 @@ func (c *Client) AuthorizeOrder(ctx context.Context, id []AuthzID, opt ...OrderO
 		Identifiers []wireAuthzID `json:"identifiers"`
 		NotBefore   string        `json:"notBefore,omitempty"`
 		NotAfter    string        `json:"notAfter,omitempty"`
+		Replaces    string        `json:"replaces,omitempty"`
 	}{}
 	for _, v := range id {
 		req.Identifiers = append(req.Identifiers, wireAuthzID{
@@ -218,6 +220,14 @@ func (c *Client) AuthorizeOrder(ctx context.Context, id []AuthzID, opt ...OrderO
 			req.NotBefore = time.Time(o).Format(time.RFC3339)
 		case orderNotAfterOpt:
 			req.NotAfter = time.Time(o).Format(time.RFC3339)
+		case orderReplacesCert:
+			req.Replaces = certRenewalIdentifier(o.cert)
+		case orderReplacesCertDER:
+			cert, err := x509.ParseCertificate(o)
+			if err != nil {
+				return nil, fmt.Errorf("failed to parse certificate being replaced: %w", err)
+			}
+			req.Replaces = certRenewalIdentifier(cert)
 		default:
 			// Package's fault if we let this happen.
 			panic(fmt.Sprintf("unsupported order option type %T", o))

+ 7 - 0
tempfork/acme/rfc8555_test.go

@@ -766,10 +766,17 @@ func TestRFC_AuthorizeOrder(t *testing.T) {
 	s.start()
 	defer s.close()
 
+	prevCertDER, _ := pem.Decode([]byte(leafPEM))
+	prevCert, err := x509.ParseCertificate(prevCertDER.Bytes)
+	if err != nil {
+		t.Fatal(err)
+	}
+
 	cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
 	o, err := cl.AuthorizeOrder(context.Background(), DomainIDs("example.org"),
 		WithOrderNotBefore(time.Date(2019, 8, 31, 0, 0, 0, 0, time.UTC)),
 		WithOrderNotAfter(time.Date(2019, 9, 2, 0, 0, 0, 0, time.UTC)),
+		WithOrderReplacesCert(prevCert),
 	)
 	if err != nil {
 		t.Fatal(err)

+ 24 - 0
tempfork/acme/types.go

@@ -391,6 +391,30 @@ type orderNotAfterOpt time.Time
 
 func (orderNotAfterOpt) privateOrderOpt() {}
 
+// WithOrderReplacesCert indicates that this Order is for a replacement of an
+// existing certificate.
+// See https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-5
+func WithOrderReplacesCert(cert *x509.Certificate) OrderOption {
+	return orderReplacesCert{cert}
+}
+
+type orderReplacesCert struct {
+	cert *x509.Certificate
+}
+
+func (orderReplacesCert) privateOrderOpt() {}
+
+// WithOrderReplacesCertDER indicates that this Order is for a replacement of
+// an existing DER-encoded certificate.
+// See https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-5
+func WithOrderReplacesCertDER(der []byte) OrderOption {
+	return orderReplacesCertDER(der)
+}
+
+type orderReplacesCertDER []byte
+
+func (orderReplacesCertDER) privateOrderOpt() {}
+
 // Authorization encodes an authorization response.
 type Authorization struct {
 	// URI uniquely identifies a authorization.