Skip to content

Commit eec23a3

Browse files
committed
acme: hardcode and remove ExternalAccountBinding.Algorithm
HMAC-SHA256 is a perfectly fine MAC algorithm, and there is no need to ask the user to choose one. This does break compatibility with the previous API, but it had been live only for a weekend, so hopefully still in a window in which we can make changes with a limited blast radius. Updates golang/go#41430 Change-Id: I03741a545b25b9fcc147760cd20e9d7029844a6c Reviewed-on: https://go-review.googlesource.com/c/crypto/+/279453 Trust: Filippo Valsorda <filippo@golang.org> Run-TryBot: Filippo Valsorda <filippo@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: James Kasten <jdkasten@google.com> Reviewed-by: Roland Shoemaker <roland@golang.org>
1 parent 9d13527 commit eec23a3

File tree

6 files changed

+35
-103
lines changed

6 files changed

+35
-103
lines changed

acme/jws.go

+21-36
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,15 @@ import (
1111
"crypto/rand"
1212
"crypto/rsa"
1313
"crypto/sha256"
14-
"crypto/sha512"
1514
_ "crypto/sha512" // need for EC keys
1615
"encoding/asn1"
1716
"encoding/base64"
1817
"encoding/json"
1918
"errors"
2019
"fmt"
21-
"hash"
2220
"math/big"
2321
)
2422

25-
// MACAlgorithm represents a JWS MAC signature algorithm.
26-
// See https://tools.ietf.org/html/rfc7518#section-3.1 for more details.
27-
type MACAlgorithm string
28-
29-
const (
30-
MACAlgorithmHS256 = MACAlgorithm("HS256")
31-
MACAlgorithmHS384 = MACAlgorithm("HS384")
32-
MACAlgorithmHS512 = MACAlgorithm("HS512")
33-
)
34-
3523
// keyID is the account identity provided by a CA during registration.
3624
type keyID string
3725

@@ -101,24 +89,35 @@ func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid keyID, nonce, ur
10189
return json.Marshal(&enc)
10290
}
10391

104-
// jwsWithMAC creates and signs a JWS using the given key and algorithm.
105-
// "rawProtected" and "rawPayload" should not be base64-URL-encoded.
106-
func jwsWithMAC(key []byte, alg MACAlgorithm, rawProtected, rawPayload []byte) (*jsonWebSignature, error) {
92+
// jwsWithMAC creates and signs a JWS using the given key and the HS256
93+
// algorithm. kid and url are included in the protected header. rawPayload
94+
// should not be base64-URL-encoded.
95+
func jwsWithMAC(key []byte, kid, url string, rawPayload []byte) (*jsonWebSignature, error) {
10796
if len(key) == 0 {
10897
return nil, errors.New("acme: cannot sign JWS with an empty MAC key")
10998
}
110-
protected := base64.RawURLEncoding.EncodeToString(rawProtected)
111-
payload := base64.RawURLEncoding.EncodeToString(rawPayload)
112-
113-
// Only HMACs are currently supported.
114-
hmac, err := newHMAC(key, alg)
99+
header := struct {
100+
Algorithm string `json:"alg"`
101+
KID string `json:"kid"`
102+
URL string `json:"url,omitempty"`
103+
}{
104+
// Only HMAC-SHA256 is supported.
105+
Algorithm: "HS256",
106+
KID: kid,
107+
URL: url,
108+
}
109+
rawProtected, err := json.Marshal(header)
115110
if err != nil {
116111
return nil, err
117112
}
118-
if _, err := hmac.Write([]byte(protected + "." + payload)); err != nil {
113+
protected := base64.RawURLEncoding.EncodeToString(rawProtected)
114+
payload := base64.RawURLEncoding.EncodeToString(rawPayload)
115+
116+
h := hmac.New(sha256.New, key)
117+
if _, err := h.Write([]byte(protected + "." + payload)); err != nil {
119118
return nil, err
120119
}
121-
mac := hmac.Sum(nil)
120+
mac := h.Sum(nil)
122121

123122
return &jsonWebSignature{
124123
Protected: protected,
@@ -218,20 +217,6 @@ func jwsHasher(pub crypto.PublicKey) (string, crypto.Hash) {
218217
return "", 0
219218
}
220219

221-
// newHMAC returns an appropriate HMAC for the given MACAlgorithm.
222-
func newHMAC(key []byte, alg MACAlgorithm) (hash.Hash, error) {
223-
switch alg {
224-
case MACAlgorithmHS256:
225-
return hmac.New(sha256.New, key), nil
226-
case MACAlgorithmHS384:
227-
return hmac.New(sha512.New384, key), nil
228-
case MACAlgorithmHS512:
229-
return hmac.New(sha512.New, key), nil
230-
default:
231-
return nil, fmt.Errorf("acme: unsupported MAC algorithm: %v", alg)
232-
}
233-
}
234-
235220
// JWKThumbprint creates a JWK thumbprint out of pub
236221
// as specified in https://tools.ietf.org/html/rfc7638.
237222
func JWKThumbprint(pub crypto.PublicKey) (string, error) {

acme/jws_test.go

+4-49
Original file line numberDiff line numberDiff line change
@@ -397,8 +397,6 @@ func TestJWSWithMAC(t *testing.T) {
397397
// Example from RFC 7520 Section 4.4.3.
398398
// https://tools.ietf.org/html/rfc7520#section-4.4.3
399399
b64Key := "hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg"
400-
alg := MACAlgorithmHS256
401-
rawProtected := []byte(`{"alg":"HS256","kid":"018c0ae5-4d9b-471b-bfd6-eef314bc7037"}`)
402400
rawPayload := []byte("It\xe2\x80\x99s a dangerous business, Frodo, going out your " +
403401
"door. You step onto the road, and if you don't keep your feet, " +
404402
"there\xe2\x80\x99s no knowing where you might be swept off " +
@@ -416,7 +414,7 @@ func TestJWSWithMAC(t *testing.T) {
416414
if err != nil {
417415
t.Fatalf("unable to decode key: %q", b64Key)
418416
}
419-
got, err := jwsWithMAC(key, alg, rawProtected, rawPayload)
417+
got, err := jwsWithMAC(key, "018c0ae5-4d9b-471b-bfd6-eef314bc7037", "", rawPayload)
420418
if err != nil {
421419
t.Fatalf("jwsWithMAC() = %q", err)
422420
}
@@ -432,22 +430,9 @@ func TestJWSWithMAC(t *testing.T) {
432430
}
433431

434432
func TestJWSWithMACError(t *testing.T) {
435-
tt := []struct {
436-
desc string
437-
alg MACAlgorithm
438-
key []byte
439-
}{
440-
{"Unknown Algorithm", MACAlgorithm("UNKNOWN-ALG"), []byte("hmac-key")},
441-
{"Empty Key", MACAlgorithmHS256, nil},
442-
}
443-
for _, tc := range tt {
444-
tc := tc
445-
t.Run(string(tc.desc), func(t *testing.T) {
446-
p := "{}"
447-
if _, err := jwsWithMAC(tc.key, tc.alg, []byte(p), []byte(p)); err == nil {
448-
t.Errorf("jwsWithMAC(%v, %v, %s, %s) = success; want err", tc.key, tc.alg, p, p)
449-
}
450-
})
433+
p := "{}"
434+
if _, err := jwsWithMAC(nil, "", "", []byte(p)); err == nil {
435+
t.Errorf("jwsWithMAC(nil, ...) = success; want err")
451436
}
452437
}
453438

@@ -525,33 +510,3 @@ func TestJWKThumbprintErrUnsupportedKey(t *testing.T) {
525510
t.Errorf("err = %q; want %q", err, ErrUnsupportedKey)
526511
}
527512
}
528-
529-
func TestNewHMAC(t *testing.T) {
530-
tt := []struct {
531-
alg MACAlgorithm
532-
wantSize int
533-
}{
534-
{MACAlgorithmHS256, 32},
535-
{MACAlgorithmHS384, 48},
536-
{MACAlgorithmHS512, 64},
537-
}
538-
for _, tc := range tt {
539-
tc := tc
540-
t.Run(string(tc.alg), func(t *testing.T) {
541-
h, err := newHMAC([]byte("key"), tc.alg)
542-
if err != nil {
543-
t.Fatalf("newHMAC(%v) = %q", tc.alg, err)
544-
}
545-
gotSize := len(h.Sum(nil))
546-
if gotSize != tc.wantSize {
547-
t.Errorf("HMAC produced signature with unexpected length; got %d want %d", gotSize, tc.wantSize)
548-
}
549-
})
550-
}
551-
}
552-
553-
func TestNewHMACError(t *testing.T) {
554-
if h, err := newHMAC([]byte("key"), MACAlgorithm("UNKNOWN-ALG")); err == nil {
555-
t.Errorf("newHMAC(UNKNOWN-ALG) = %T, nil; want error", h)
556-
}
557-
}

acme/rfc8555.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
package acme
66

77
import (
8-
"bytes"
98
"context"
109
"crypto"
1110
"encoding/base64"
@@ -93,9 +92,7 @@ func (c *Client) encodeExternalAccountBinding(eab *ExternalAccountBinding) (*jso
9392
if err != nil {
9493
return nil, err
9594
}
96-
var rProtected bytes.Buffer
97-
fmt.Fprintf(&rProtected, `{"alg":%q,"kid":%q,"url":%q}`, eab.Algorithm, eab.KID, c.dir.RegURL)
98-
return jwsWithMAC(eab.Key, eab.Algorithm, rProtected.Bytes(), []byte(jwk))
95+
return jwsWithMAC(eab.Key, eab.KID, c.dir.RegURL, []byte(jwk))
9996
}
10097

10198
// updateRegRFC is equivalent to c.UpdateReg but for CAs implementing RFC 8555.

acme/rfc8555_test.go

+5-6
Original file line numberDiff line numberDiff line change
@@ -351,9 +351,8 @@ func TestRFC_Register(t *testing.T) {
351351

352352
func TestRFC_RegisterExternalAccountBinding(t *testing.T) {
353353
eab := &ExternalAccountBinding{
354-
KID: "kid-1",
355-
Key: []byte("secret"),
356-
Algorithm: MACAlgorithmHS256,
354+
KID: "kid-1",
355+
Key: []byte("secret"),
357356
}
358357

359358
type protected struct {
@@ -403,10 +402,10 @@ func TestRFC_RegisterExternalAccountBinding(t *testing.T) {
403402
if prot.KID != eab.KID {
404403
t.Errorf("j.ExternalAccountBinding.KID = %s; want %s", prot.KID, eab.KID)
405404
}
406-
// Ensure same Algorithm.
407-
if prot.Algorithm != string(eab.Algorithm) {
405+
// Ensure expected Algorithm.
406+
if prot.Algorithm != "HS256" {
408407
t.Errorf("j.ExternalAccountBinding.Alg = %s; want %s",
409-
prot.Algorithm, eab.Algorithm)
408+
prot.Algorithm, "HS256")
410409
}
411410

412411
// Ensure same URL as outer JWS.

acme/types.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -217,13 +217,10 @@ type ExternalAccountBinding struct {
217217
// Key is the bytes of the symmetric key that the CA provides to identify
218218
// the account. Key must correspond to the KID.
219219
Key []byte
220-
221-
// Algorithm used to sign the JWS.
222-
Algorithm MACAlgorithm
223220
}
224221

225222
func (e *ExternalAccountBinding) String() string {
226-
return fmt.Sprintf("&{KID: %q, Key: redacted, Algorithm: %v}", e.KID, e.Algorithm)
223+
return fmt.Sprintf("&{KID: %q, Key: redacted}", e.KID)
227224
}
228225

229226
// Directory is ACME server discovery data.

acme/types_test.go

+3-4
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,11 @@ import (
1313

1414
func TestExternalAccountBindingString(t *testing.T) {
1515
eab := ExternalAccountBinding{
16-
KID: "kid",
17-
Key: []byte("key"),
18-
Algorithm: MACAlgorithmHS256,
16+
KID: "kid",
17+
Key: []byte("key"),
1918
}
2019
got := eab.String()
21-
want := `&{KID: "kid", Key: redacted, Algorithm: HS256}`
20+
want := `&{KID: "kid", Key: redacted}`
2221
if got != want {
2322
t.Errorf("eab.String() = %q, want: %q", got, want)
2423
}

0 commit comments

Comments
 (0)