Refactor creation of (raw) EAB JWS contents

This commit is contained in:
Herman Slatman 2021-12-07 14:57:39 +01:00
parent 23898e9b76
commit 6e11657204
No known key found for this signature in database
GPG key ID: F4D8A44EA0A75A4F

View file

@ -3,16 +3,9 @@ package api
import (
"bytes"
"context"
"crypto"
"crypto/ecdsa"
"crypto/hmac"
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"math/big"
"net/http/httptest"
"net/url"
"testing"
@ -57,125 +50,55 @@ func newACMEProv(t *testing.T) *provisioner.ACME {
return a
}
var errUnsupportedKey = fmt.Errorf("unknown key type; only RSA and ECDSA are supported")
// keyID is the account 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("")
// jwsEncodeEAB creates a JWS payload for External Account Binding according to RFC 8555 §7.3.4.
// Implementation taken from github.com/mholt/acmez
func jwsEncodeEAB(accountKey crypto.PublicKey, hmacKey []byte, kid keyID, u string) ([]byte, error) {
// §7.3.4: "The 'alg' field MUST indicate a MAC-based algorithm"
alg, sha := "HS256", crypto.SHA256
// §7.3.4: "The 'nonce' field MUST NOT be present"
phead, err := jwsHead(alg, "", u, kid, nil)
func createEABJWS(jwk *jose.JSONWebKey, hmacKey []byte, keyID string, url string) (*jose.JSONWebSignature, error) {
signer, err := jose.NewSigner(
jose.SigningKey{
Algorithm: jose.SignatureAlgorithm("HS256"),
Key: hmacKey,
},
&jose.SignerOptions{
ExtraHeaders: map[jose.HeaderKey]interface{}{
"kid": keyID,
"url": url,
},
EmbedJWK: false,
},
)
if err != nil {
return nil, err
}
encodedKey, err := jwkEncode(accountKey)
jwkJSONBytes, err := jwk.Public().MarshalJSON()
if err != nil {
return nil, err
}
payload := base64.RawURLEncoding.EncodeToString([]byte(encodedKey))
payloadToSign := []byte(phead + "." + payload)
h := hmac.New(sha256.New, hmacKey)
h.Write(payloadToSign)
sig := h.Sum(nil)
return jwsFinal(sha, sig, phead, payload)
}
// jwsHead constructs the protected JWS header for the given fields.
// Since jwk and kid are mutually-exclusive, the jwk will be encoded
// only if kid is empty. If nonce is empty, it will not be encoded.
// Implementation taken from github.com/mholt/acmez
func jwsHead(alg, nonce, u string, kid keyID, key crypto.Signer) (string, error) {
phead := fmt.Sprintf(`{"alg":%q`, alg)
if kid == noKeyID {
jwk, err := jwkEncode(key.Public())
if err != nil {
return "", err
}
phead += fmt.Sprintf(`,"jwk":%s`, jwk)
} else {
phead += fmt.Sprintf(`,"kid":%q`, kid)
}
if nonce != "" {
phead += fmt.Sprintf(`,"nonce":%q`, nonce)
}
phead += fmt.Sprintf(`,"url":%q}`, u)
phead = base64.RawURLEncoding.EncodeToString([]byte(phead))
return phead, nil
}
// jwkEncode encodes public part of an RSA or ECDSA key into a JWK.
// The result is also suitable for creating a JWK thumbprint.
// https://tools.ietf.org/html/rfc7517
// Implementation taken from github.com/mholt/acmez
func jwkEncode(pub crypto.PublicKey) (string, error) {
switch pub := pub.(type) {
case *rsa.PublicKey:
// https://tools.ietf.org/html/rfc7518#section-6.3.1
n := pub.N
e := big.NewInt(int64(pub.E))
// Field order is important.
// See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
return fmt.Sprintf(`{"e":%q,"kty":"RSA","n":%q}`,
base64.RawURLEncoding.EncodeToString(e.Bytes()),
base64.RawURLEncoding.EncodeToString(n.Bytes()),
), nil
case *ecdsa.PublicKey:
// https://tools.ietf.org/html/rfc7518#section-6.2.1
p := pub.Curve.Params()
n := p.BitSize / 8
if p.BitSize%8 != 0 {
n++
}
x := pub.X.Bytes()
if n > len(x) {
x = append(make([]byte, n-len(x)), x...)
}
y := pub.Y.Bytes()
if n > len(y) {
y = append(make([]byte, n-len(y)), y...)
}
// Field order is important.
// See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
return fmt.Sprintf(`{"crv":%q,"kty":"EC","x":%q,"y":%q}`,
p.Name,
base64.RawURLEncoding.EncodeToString(x),
base64.RawURLEncoding.EncodeToString(y),
), nil
}
return "", errUnsupportedKey
}
// jwsFinal constructs the final JWS object.
// Implementation taken from github.com/mholt/acmez
func jwsFinal(sha crypto.Hash, sig []byte, phead, payload string) ([]byte, error) {
enc := struct {
Protected string `json:"protected"`
Payload string `json:"payload"`
Sig string `json:"signature"`
}{
Protected: phead,
Payload: payload,
Sig: base64.RawURLEncoding.EncodeToString(sig),
}
result, err := json.Marshal(&enc)
jws, err := signer.Sign(jwkJSONBytes)
if err != nil {
return nil, err
}
return result, nil
raw, err := jws.CompactSerialize()
if err != nil {
return nil, err
}
parsedJWS, err := jose.ParseJWS(raw)
if err != nil {
return nil, err
}
return parsedJWS, nil
}
func createRawEABJWS(jwk *jose.JSONWebKey, hmacKey []byte, keyID string, url string) ([]byte, error) {
jws, err := createEABJWS(jwk, hmacKey, keyID, url)
if err != nil {
return nil, err
}
rawJWS := jws.FullSerialize()
return []byte(rawJWS), nil
}
func TestNewAccountRequest_Validate(t *testing.T) {
@ -565,10 +488,11 @@ func TestHandler_NewAccount(t *testing.T) {
"fail/acmeProvisionerFromContext": func(t *testing.T) test {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName))
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -597,10 +521,10 @@ func TestHandler_NewAccount(t *testing.T) {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", url)
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -723,10 +647,11 @@ func TestHandler_NewAccount(t *testing.T) {
"ok/new-account-no-eab-required": func(t *testing.T) test {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName))
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -764,10 +689,10 @@ func TestHandler_NewAccount(t *testing.T) {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", url)
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -1142,10 +1067,11 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
"ok/no-eab-required-but-provided": func(t *testing.T) test {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName))
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
prov := newACMEProv(t)
ctx := context.WithValue(context.Background(), jwkContextKey, jwk)
@ -1166,10 +1092,10 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", url)
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -1228,10 +1154,11 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
"fail/acmeProvisionerFromContext": func(t *testing.T) test {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName))
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -1258,10 +1185,11 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
"fail/parse-eab-jose": func(t *testing.T) test {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName))
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
eab.Payload += "{}"
prov := newACMEProv(t)
@ -1284,10 +1212,10 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", url)
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -1331,10 +1259,10 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", url)
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -1379,10 +1307,10 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", url)
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -1429,10 +1357,10 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", url)
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -1479,10 +1407,10 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", url)
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -1527,10 +1455,10 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", url)
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -1586,10 +1514,10 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", url)
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -1644,10 +1572,10 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
differentJWK, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := jwsEncodeEAB(differentJWK.Public().Key, []byte{1, 3, 3, 7}, "eakID", url)
rawEABJWS, err := createRawEABJWS(differentJWK, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -1700,10 +1628,10 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", url)
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -1755,10 +1683,10 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", url)
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
err = json.Unmarshal(rawEABJWS, &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -1868,109 +1796,86 @@ func Test_validateEABJWS(t *testing.T) {
"fail/invalid-number-of-signatures": func(t *testing.T) test {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName))
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := createEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
assert.FatalError(t, err)
parsedEABJWS, err := jose.ParseJWS(string(eabJWS))
assert.FatalError(t, err)
parsedEABJWS.Signatures = append(parsedEABJWS.Signatures, jose.Signature{})
eabJWS.Signatures = append(eabJWS.Signatures, jose.Signature{})
return test{
jws: parsedEABJWS,
jws: eabJWS,
err: acme.NewError(acme.ErrorMalformedType, "JWS must have one signature"),
}
},
"fail/invalid-algorithm": func(t *testing.T) test {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName))
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := createEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
assert.FatalError(t, err)
parsedEABJWS, err := jose.ParseJWS(string(eabJWS))
assert.FatalError(t, err)
parsedEABJWS.Signatures[0].Protected.Algorithm = "HS42"
eabJWS.Signatures[0].Protected.Algorithm = "HS42"
return test{
jws: parsedEABJWS,
jws: eabJWS,
err: acme.NewError(acme.ErrorMalformedType, "'alg' field set to invalid algorithm 'HS42'"),
}
},
"fail/kid-not-set": func(t *testing.T) test {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName))
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := createEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
assert.FatalError(t, err)
parsedEABJWS, err := jose.ParseJWS(string(eabJWS))
assert.FatalError(t, err)
parsedEABJWS.Signatures[0].Protected.KeyID = ""
eabJWS.Signatures[0].Protected.KeyID = ""
return test{
jws: parsedEABJWS,
jws: eabJWS,
err: acme.NewError(acme.ErrorMalformedType, "'kid' field is required"),
}
},
"fail/nonce-not-empty": func(t *testing.T) test {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName))
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := createEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
assert.FatalError(t, err)
parsedEABJWS, err := jose.ParseJWS(string(eabJWS))
assert.FatalError(t, err)
parsedEABJWS.Signatures[0].Protected.Nonce = "some-bogus-nonce"
eabJWS.Signatures[0].Protected.Nonce = "some-bogus-nonce"
return test{
jws: parsedEABJWS,
jws: eabJWS,
err: acme.NewError(acme.ErrorMalformedType, "'nonce' must not be present"),
}
},
"fail/url-not-set": func(t *testing.T) test {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName))
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := createEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
assert.FatalError(t, err)
parsedEABJWS, err := jose.ParseJWS(string(eabJWS))
assert.FatalError(t, err)
delete(parsedEABJWS.Signatures[0].Protected.ExtraHeaders, "url")
delete(eabJWS.Signatures[0].Protected.ExtraHeaders, "url")
return test{
jws: parsedEABJWS,
jws: eabJWS,
err: acme.NewError(acme.ErrorMalformedType, "'url' field is required"),
}
},
"fail/no-outer-jws": func(t *testing.T) test {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName))
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
assert.FatalError(t, err)
parsedEABJWS, err := jose.ParseJWS(string(eabJWS))
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := createEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
ctx := context.WithValue(context.TODO(), jwsContextKey, nil)
return test{
ctx: ctx,
jws: parsedEABJWS,
jws: eabJWS,
err: acme.NewErrorISE("could not retrieve outer JWS from context"),
}
},
"fail/outer-jws-multiple-signatures": func(t *testing.T) test {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName))
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := createEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
rawEABJWS := eabJWS.FullSerialize()
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
assert.FatalError(t, err)
parsedEABJWS, err := jose.ParseJWS(string(eabJWS))
err = json.Unmarshal([]byte(rawEABJWS), &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -1995,19 +1900,20 @@ func Test_validateEABJWS(t *testing.T) {
ctx := context.WithValue(context.TODO(), jwsContextKey, outerJWS)
return test{
ctx: ctx,
jws: parsedEABJWS,
jws: eabJWS,
err: acme.NewError(acme.ErrorMalformedType, "outer JWS must have one signature"),
}
},
"fail/outer-jws-no-url": func(t *testing.T) test {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName))
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := createEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
rawEABJWS := eabJWS.FullSerialize()
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
assert.FatalError(t, err)
parsedEABJWS, err := jose.ParseJWS(string(eabJWS))
err = json.Unmarshal([]byte(rawEABJWS), &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -2031,19 +1937,20 @@ func Test_validateEABJWS(t *testing.T) {
ctx := context.WithValue(context.TODO(), jwsContextKey, outerJWS)
return test{
ctx: ctx,
jws: parsedEABJWS,
jws: eabJWS,
err: acme.NewError(acme.ErrorMalformedType, "'url' field must be set in outer JWS"),
}
},
"fail/outer-jws-with-different-url": func(t *testing.T) test {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName))
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := createEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
rawEABJWS := eabJWS.FullSerialize()
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
assert.FatalError(t, err)
parsedEABJWS, err := jose.ParseJWS(string(eabJWS))
err = json.Unmarshal([]byte(rawEABJWS), &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -2068,7 +1975,7 @@ func Test_validateEABJWS(t *testing.T) {
ctx := context.WithValue(context.TODO(), jwsContextKey, outerJWS)
return test{
ctx: ctx,
jws: parsedEABJWS,
jws: eabJWS,
err: acme.NewError(acme.ErrorMalformedType, "'url' field is not the same value as the outer JWS"),
}
},
@ -2076,12 +1983,12 @@ func Test_validateEABJWS(t *testing.T) {
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
assert.FatalError(t, err)
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
eabJWS, err := jwsEncodeEAB(jwk.Public().Key, []byte{1, 3, 3, 7}, "eakID", url)
eabJWS, err := createEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
assert.FatalError(t, err)
rawEABJWS := eabJWS.FullSerialize()
assert.FatalError(t, err)
eab := &ExternalAccountBinding{}
err = json.Unmarshal(eabJWS, &eab)
assert.FatalError(t, err)
parsedEABJWS, err := jose.ParseJWS(string(eabJWS))
err = json.Unmarshal([]byte(rawEABJWS), &eab)
assert.FatalError(t, err)
nar := &NewAccountRequest{
Contact: []string{"foo", "bar"},
@ -2106,7 +2013,7 @@ func Test_validateEABJWS(t *testing.T) {
ctx := context.WithValue(context.TODO(), jwsContextKey, outerJWS)
return test{
ctx: ctx,
jws: parsedEABJWS,
jws: eabJWS,
keyID: "eakID",
err: nil,
}