From d877807ef67634d04e701744949bf2506fd0863e Mon Sep 17 00:00:00 2001 From: Roland Shoemaker Date: Fri, 8 Oct 2021 11:41:44 -0700 Subject: [PATCH] acme: expose Client KID field Expose the previously private KID field of the Client type. This allows callers which have locally cached their key identity to avoid needing to make a call to the ACME service every time they construct a new client. Fixes golang/go#46303 Change-Id: I219167c5b941f56a2028c4bc253ff56386845549 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/354697 Trust: Katie Hockman Reviewed-by: Katie Hockman Trust: Roland Shoemaker Run-TryBot: Roland Shoemaker TryBot-Result: Gopher Robot --- acme/acme.go | 14 ++++++++------ acme/jws.go | 10 +++++----- acme/jws_test.go | 2 +- acme/rfc8555.go | 2 +- acme/rfc8555_test.go | 6 +++--- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/acme/acme.go b/acme/acme.go index 73b19ef35a..271df26164 100644 --- a/acme/acme.go +++ b/acme/acme.go @@ -125,7 +125,9 @@ type Client struct { cacheMu sync.Mutex dir *Directory // cached result of Client's Discover method - kid keyID // cached Account.URI obtained from registerRFC or getAccountRFC + // KID is the key identifier provided by the CA. If not provided it will be + // retrieved from the CA by making a call to the registration endpoint. + KID KeyID noncesMu sync.Mutex nonces map[string]struct{} // nonces collected from previous responses @@ -140,21 +142,21 @@ type Client struct { // // When in pre-RFC mode or when c.getRegRFC responds with an error, accountKID // returns noKeyID. -func (c *Client) accountKID(ctx context.Context) keyID { +func (c *Client) accountKID(ctx context.Context) KeyID { c.cacheMu.Lock() defer c.cacheMu.Unlock() if !c.dir.rfcCompliant() { return noKeyID } - if c.kid != noKeyID { - return c.kid + if c.KID != noKeyID { + return c.KID } a, err := c.getRegRFC(ctx) if err != nil { return noKeyID } - c.kid = keyID(a.URI) - return c.kid + c.KID = KeyID(a.URI) + return c.KID } // Discover performs ACME server discovery using c.DirectoryURL. diff --git a/acme/jws.go b/acme/jws.go index 8c3ecceca7..8a097da05d 100644 --- a/acme/jws.go +++ b/acme/jws.go @@ -20,12 +20,12 @@ import ( "math/big" ) -// keyID is the account identity provided by a CA during registration. -type keyID string +// KeyID is the account key identity provided by a CA during registration. +type KeyID string // noKeyID indicates that jwsEncodeJSON should compute and use JWK instead of a KID. // See jwsEncodeJSON for details. -const noKeyID = keyID("") +const noKeyID = KeyID("") // noPayload indicates jwsEncodeJSON will encode zero-length octet string // in a JWS request. This is called POST-as-GET in RFC 8555 and is used to make @@ -43,14 +43,14 @@ type jsonWebSignature struct { // jwsEncodeJSON signs claimset using provided key and a nonce. // The result is serialized in JSON format containing either kid or jwk -// fields based on the provided keyID value. +// fields based on the provided KeyID value. // // If kid is non-empty, its quoted value is inserted in the protected head // as "kid" field value. Otherwise, JWK is computed using jwkEncode and inserted // as "jwk" field value. The "jwk" and "kid" fields are mutually exclusive. // // See https://tools.ietf.org/html/rfc7515#section-7. -func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid keyID, nonce, url string) ([]byte, error) { +func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid KeyID, nonce, url string) ([]byte, error) { alg, sha := jwsHasher(key.Public()) if alg == "" || !sha.Available() { return nil, ErrUnsupportedKey diff --git a/acme/jws_test.go b/acme/jws_test.go index c8a1e8b31a..738f1efba5 100644 --- a/acme/jws_test.go +++ b/acme/jws_test.go @@ -196,7 +196,7 @@ func TestJWSEncodeJSON(t *testing.T) { } func TestJWSEncodeKID(t *testing.T) { - kid := keyID("https://example.org/account/1") + kid := KeyID("https://example.org/account/1") claims := struct{ Msg string }{"Hello JWS"} // JWS signed with testKeyEC const ( diff --git a/acme/rfc8555.go b/acme/rfc8555.go index f9d3011ffc..928a5aa036 100644 --- a/acme/rfc8555.go +++ b/acme/rfc8555.go @@ -78,7 +78,7 @@ func (c *Client) registerRFC(ctx context.Context, acct *Account, prompt func(tos } // Cache Account URL even if we return an error to the caller. // It is by all means a valid and usable "kid" value for future requests. - c.kid = keyID(a.URI) + c.KID = KeyID(a.URI) if res.StatusCode == http.StatusOK { return nil, ErrAccountAlreadyExists } diff --git a/acme/rfc8555_test.go b/acme/rfc8555_test.go index 07e2f29db2..4882759328 100644 --- a/acme/rfc8555_test.go +++ b/acme/rfc8555_test.go @@ -344,7 +344,7 @@ func TestRFC_Register(t *testing.T) { if !didPrompt { t.Error("tos prompt wasn't called") } - if v := cl.accountKID(ctx); v != keyID(okAccount.URI) { + if v := cl.accountKID(ctx); v != KeyID(okAccount.URI) { t.Errorf("account kid = %q; want %q", v, okAccount.URI) } } @@ -482,7 +482,7 @@ func TestRFC_RegisterExternalAccountBinding(t *testing.T) { if !didPrompt { t.Error("tos prompt wasn't called") } - if v := cl.accountKID(ctx); v != keyID(okAccount.URI) { + if v := cl.accountKID(ctx); v != KeyID(okAccount.URI) { t.Errorf("account kid = %q; want %q", v, okAccount.URI) } } @@ -502,7 +502,7 @@ func TestRFC_RegisterExisting(t *testing.T) { if err != ErrAccountAlreadyExists { t.Errorf("err = %v; want %v", err, ErrAccountAlreadyExists) } - kid := keyID(s.url("/accounts/1")) + kid := KeyID(s.url("/accounts/1")) if v := cl.accountKID(context.Background()); v != kid { t.Errorf("account kid = %q; want %q", v, kid) }