Move new code to a different file.
This commit is contained in:
parent
3d3b508ad3
commit
45f1e2958c
|
@ -0,0 +1,48 @@
|
||||||
|
package wfe
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/letsencrypt/go-jose"
|
||||||
|
"github.com/letsencrypt/boulder/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func algorithmForKey(key *jose.JsonWebKey) (string, error) {
|
||||||
|
// TODO(https://github.com/letsencrypt/boulder/issues/792): Support EC.
|
||||||
|
switch key.Key.(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
return string(jose.RS256), nil
|
||||||
|
}
|
||||||
|
return "", core.SignatureValidationError("no signature algorithms suitable for given key type")
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
noAlgorithmForKey = "WFE.Errors.NoAlgorithmForKey"
|
||||||
|
invalidJWSAlgorithm = "WFE.Errors.InvalidJWSAlgorithm"
|
||||||
|
invalidAlgorithmOnKey = "WFE.Errors.InvalidAlgorithmOnKey"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check that (1) there is a suitable algorithm for the provided key based on its
|
||||||
|
// Golang type, (2) the Algorithm field on the JWK is either absent, or matches
|
||||||
|
// that algorithm, and (3) the Algorithm field on the JWK is present and matches
|
||||||
|
// that algorithm. Precondition: parsedJws must have exactly one signature on
|
||||||
|
// it. Returns stat name to increment if err is non-nil.
|
||||||
|
func checkAlgorithm(key *jose.JsonWebKey, parsedJws *jose.JsonWebSignature) (string, error) {
|
||||||
|
algorithm, err := algorithmForKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return noAlgorithmForKey, err
|
||||||
|
}
|
||||||
|
jwsAlgorithm := parsedJws.Signatures[0].Header.Algorithm
|
||||||
|
if jwsAlgorithm != algorithm {
|
||||||
|
return invalidJWSAlgorithm,
|
||||||
|
core.SignatureValidationError(fmt.Sprintf(
|
||||||
|
"algorithm '%s' in JWS header not acceptable", jwsAlgorithm))
|
||||||
|
}
|
||||||
|
if key.Algorithm != "" && key.Algorithm != algorithm {
|
||||||
|
return invalidAlgorithmOnKey,
|
||||||
|
core.SignatureValidationError(fmt.Sprintf(
|
||||||
|
"algorithm '%s' on JWK is unacceptable", key.Algorithm))
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
|
@ -0,0 +1,176 @@
|
||||||
|
package wfe
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rsa"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/letsencrypt/go-jose"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRejectsNone(t *testing.T) {
|
||||||
|
wfe, _ := setupWFE(t)
|
||||||
|
_, _, _, err := wfe.verifyPOST(newRequestEvent(), makePostRequest(`
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"alg": "none",
|
||||||
|
"jwk": {
|
||||||
|
"kty": "RSA",
|
||||||
|
"n": "vrjT",
|
||||||
|
"e": "AQAB"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"payload": "aGkK",
|
||||||
|
"signature": ""
|
||||||
|
}
|
||||||
|
`), true, "foo")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("verifyPOST did not reject JWS with alg: 'none'")
|
||||||
|
}
|
||||||
|
if err.Error() != "algorithm 'none' in JWS header not acceptable" {
|
||||||
|
t.Fatalf("verifyPOST rejected JWS with alg: 'none', but for wrong reason: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRejectsHS256(t *testing.T) {
|
||||||
|
wfe, _ := setupWFE(t)
|
||||||
|
_, _, _, err := wfe.verifyPOST(newRequestEvent(), makePostRequest(`
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"alg": "HS256",
|
||||||
|
"jwk": {
|
||||||
|
"kty": "RSA",
|
||||||
|
"n": "vrjT",
|
||||||
|
"e": "AQAB"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"payload": "aGkK",
|
||||||
|
"signature": ""
|
||||||
|
}
|
||||||
|
`), true, "foo")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("verifyPOST did not reject JWS with alg: 'HS256'")
|
||||||
|
}
|
||||||
|
expected := "algorithm 'HS256' in JWS header not acceptable"
|
||||||
|
if err.Error() != expected {
|
||||||
|
t.Fatalf("verifyPOST rejected JWS with alg: 'none', but for wrong reason: got '%s', wanted %s", err, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckAlgorithm(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
key jose.JsonWebKey
|
||||||
|
jws jose.JsonWebSignature
|
||||||
|
expectedErr string
|
||||||
|
expectedStat string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
jose.JsonWebKey{
|
||||||
|
Algorithm: "ES256",
|
||||||
|
Key: &ecdsa.PublicKey{},
|
||||||
|
},
|
||||||
|
jose.JsonWebSignature{},
|
||||||
|
"no signature algorithms suitable for given key type",
|
||||||
|
"WFE.Errors.NoAlgorithmForKey",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jose.JsonWebKey{
|
||||||
|
Algorithm: "HS256",
|
||||||
|
},
|
||||||
|
jose.JsonWebSignature{},
|
||||||
|
"no signature algorithms suitable for given key type",
|
||||||
|
"WFE.Errors.NoAlgorithmForKey",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jose.JsonWebKey{
|
||||||
|
Key: &rsa.PublicKey{},
|
||||||
|
},
|
||||||
|
jose.JsonWebSignature{
|
||||||
|
Signatures: []jose.Signature{
|
||||||
|
jose.Signature{
|
||||||
|
Header: jose.JoseHeader{
|
||||||
|
Algorithm: "HS256",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"algorithm 'HS256' in JWS header not acceptable",
|
||||||
|
"WFE.Errors.InvalidJWSAlgorithm",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jose.JsonWebKey{
|
||||||
|
Algorithm: "HS256",
|
||||||
|
Key: &rsa.PublicKey{},
|
||||||
|
},
|
||||||
|
jose.JsonWebSignature{
|
||||||
|
Signatures: []jose.Signature{
|
||||||
|
jose.Signature{
|
||||||
|
Header: jose.JoseHeader{
|
||||||
|
Algorithm: "HS256",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"algorithm 'HS256' in JWS header not acceptable",
|
||||||
|
"WFE.Errors.InvalidJWSAlgorithm",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jose.JsonWebKey{
|
||||||
|
Algorithm: "HS256",
|
||||||
|
Key: &rsa.PublicKey{},
|
||||||
|
},
|
||||||
|
jose.JsonWebSignature{
|
||||||
|
Signatures: []jose.Signature{
|
||||||
|
jose.Signature{
|
||||||
|
Header: jose.JoseHeader{
|
||||||
|
Algorithm: "RS256",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"algorithm 'HS256' on JWK is unacceptable",
|
||||||
|
"WFE.Errors.InvalidAlgorithmOnKey",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i, tc := range testCases {
|
||||||
|
stat, err := checkAlgorithm(&tc.key, &tc.jws)
|
||||||
|
if tc.expectedErr != "" && err.Error() != tc.expectedErr {
|
||||||
|
t.Errorf("TestCheckAlgorithm %d: Expected '%s', got '%s'", i, tc.expectedErr, err)
|
||||||
|
}
|
||||||
|
if tc.expectedStat != "" && stat != tc.expectedStat {
|
||||||
|
t.Errorf("TestCheckAlgorithm %d: Expected stat '%s', got '%s'", i, tc.expectedStat, stat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckAlgorithmSuccess(t *testing.T) {
|
||||||
|
_, err := checkAlgorithm(&jose.JsonWebKey{
|
||||||
|
Algorithm: "RS256",
|
||||||
|
Key: &rsa.PublicKey{},
|
||||||
|
}, &jose.JsonWebSignature{
|
||||||
|
Signatures: []jose.Signature{
|
||||||
|
jose.Signature{
|
||||||
|
Header: jose.JoseHeader{
|
||||||
|
Algorithm: "RS256",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("RS256 key: Expected nil error, got '%s'", err)
|
||||||
|
}
|
||||||
|
_, err = checkAlgorithm(&jose.JsonWebKey{
|
||||||
|
Key: &rsa.PublicKey{},
|
||||||
|
}, &jose.JsonWebSignature{
|
||||||
|
Signatures: []jose.Signature{
|
||||||
|
jose.Signature{
|
||||||
|
Header: jose.JoseHeader{
|
||||||
|
Algorithm: "RS256",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("RS256 key: Expected nil error, got '%s'", err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,6 @@ package wfe
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -317,38 +316,6 @@ const (
|
||||||
malformedJWS = "Unable to read/verify body"
|
malformedJWS = "Unable to read/verify body"
|
||||||
)
|
)
|
||||||
|
|
||||||
func algorithmForKey(key *jose.JsonWebKey) (string, error) {
|
|
||||||
// TODO(https://github.com/letsencrypt/boulder/issues/792): Support EC.
|
|
||||||
switch key.Key.(type) {
|
|
||||||
case *rsa.PublicKey:
|
|
||||||
return "RS256", nil
|
|
||||||
}
|
|
||||||
return "", core.SignatureValidationError("no signature algorithms suitable for given key type")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that (1) there is a suitable algorithm for the provided key based on its
|
|
||||||
// Golang type, (2) the Algorithm field on the JWK is either absent, or matches
|
|
||||||
// that algorithm, and (3) the Algorithm field on the JWK is present and matches
|
|
||||||
// that algorithm.
|
|
||||||
func checkAlgorithm(key *jose.JsonWebKey, parsedJws *jose.JsonWebSignature) (string, error) {
|
|
||||||
algorithm, err := algorithmForKey(key)
|
|
||||||
if err != nil {
|
|
||||||
return "NoAlgorithmForKey", err
|
|
||||||
}
|
|
||||||
jwsAlgorithm := parsedJws.Signatures[0].Header.Algorithm
|
|
||||||
if jwsAlgorithm != algorithm {
|
|
||||||
return "InvalidJWSAlgorithm",
|
|
||||||
core.SignatureValidationError(fmt.Sprintf(
|
|
||||||
"algorithm '%s' in JWS header not acceptable", jwsAlgorithm))
|
|
||||||
}
|
|
||||||
if key.Algorithm != "" && key.Algorithm != algorithm {
|
|
||||||
return "InvalidAlgorithmOnKey",
|
|
||||||
core.SignatureValidationError(fmt.Sprintf(
|
|
||||||
"algorithm '%s' on JWK is unacceptable", key.Algorithm))
|
|
||||||
}
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// verifyPOST reads and parses the request body, looks up the Registration
|
// verifyPOST reads and parses the request body, looks up the Registration
|
||||||
// corresponding to its JWK, verifies the JWS signature, checks that the
|
// corresponding to its JWK, verifies the JWS signature, checks that the
|
||||||
// resource field is present and correct in the JWS protected header, and
|
// resource field is present and correct in the JWS protected header, and
|
||||||
|
@ -456,7 +423,7 @@ func (wfe *WebFrontEndImpl) verifyPOST(logEvent *requestEvent, request *http.Req
|
||||||
}
|
}
|
||||||
|
|
||||||
if statName, err := checkAlgorithm(key, parsedJws); err != nil {
|
if statName, err := checkAlgorithm(key, parsedJws); err != nil {
|
||||||
wfe.stats.Inc(fmt.Sprintf("WFE.Errors.%s", statName), 1, 1.0)
|
wfe.stats.Inc(statName, 1, 1.0)
|
||||||
return nil, nil, reg, err
|
return nil, nil, reg, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ package wfe
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -1388,157 +1387,6 @@ func TestVerifyPOSTUsesStoredKey(t *testing.T) {
|
||||||
test.AssertError(t, err, "No error returned when provided key differed from stored key.")
|
test.AssertError(t, err, "No error returned when provided key differed from stored key.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRejectsNone(t *testing.T) {
|
|
||||||
wfe, _ := setupWFE(t)
|
|
||||||
_, _, _, err := wfe.verifyPOST(newRequestEvent(), makePostRequest(`
|
|
||||||
{
|
|
||||||
"header": {
|
|
||||||
"alg": "none",
|
|
||||||
"jwk": {
|
|
||||||
"kty": "RSA",
|
|
||||||
"n": "vrjT",
|
|
||||||
"e": "AQAB"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"payload": "aGkK",
|
|
||||||
"signature": ""
|
|
||||||
}
|
|
||||||
`), true, "foo")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("verifyPOST did not reject JWS with alg: 'none'")
|
|
||||||
}
|
|
||||||
if err.Error() != "algorithm 'none' in JWS header not acceptable" {
|
|
||||||
t.Fatalf("verifyPOST rejected JWS with alg: 'none', but for wrong reason: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRejectsHS256(t *testing.T) {
|
|
||||||
wfe, _ := setupWFE(t)
|
|
||||||
_, _, _, err := wfe.verifyPOST(newRequestEvent(), makePostRequest(`
|
|
||||||
{
|
|
||||||
"header": {
|
|
||||||
"alg": "HS256",
|
|
||||||
"jwk": {
|
|
||||||
"kty": "RSA",
|
|
||||||
"n": "vrjT",
|
|
||||||
"e": "AQAB"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"payload": "aGkK",
|
|
||||||
"signature": ""
|
|
||||||
}
|
|
||||||
`), true, "foo")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("verifyPOST did not reject JWS with alg: 'HS256'")
|
|
||||||
}
|
|
||||||
expected := "algorithm 'HS256' in JWS header not acceptable"
|
|
||||||
if err.Error() != expected {
|
|
||||||
t.Fatalf("verifyPOST rejected JWS with alg: 'none', but for wrong reason: got '%s', wanted %s", err, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCheckAlgorithmES(t *testing.T) {
|
|
||||||
stat, err := checkAlgorithm(&jose.JsonWebKey{
|
|
||||||
Algorithm: "ES256",
|
|
||||||
Key: &ecdsa.PublicKey{},
|
|
||||||
}, &jose.JsonWebSignature{})
|
|
||||||
expected := "no signature algorithms suitable for given key type"
|
|
||||||
if err == nil || err.Error() != expected {
|
|
||||||
t.Errorf("ES256 key: Expected error '%s', got '%s'", expected, err)
|
|
||||||
}
|
|
||||||
if stat != "NoAlgorithmForKey" {
|
|
||||||
t.Errorf("ES256 key: Expected stat '%s', got '%s'", "NoAlgorithmForKey", stat)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCheckAlgorithmHS(t *testing.T) {
|
|
||||||
stat, err := checkAlgorithm(&jose.JsonWebKey{
|
|
||||||
Algorithm: "HS256",
|
|
||||||
}, &jose.JsonWebSignature{})
|
|
||||||
expected := "no signature algorithms suitable for given key type"
|
|
||||||
if err == nil || err.Error() != expected {
|
|
||||||
t.Errorf("HS256 key: Expected error '%s', got '%s'", expected, err)
|
|
||||||
}
|
|
||||||
if stat != "NoAlgorithmForKey" {
|
|
||||||
t.Errorf("HS256 key: Expected stat '%s', got '%s'", "NoAlgorithmForKey", stat)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCheckAlgorithmBadJWS(t *testing.T) {
|
|
||||||
stat, err := checkAlgorithm(&jose.JsonWebKey{
|
|
||||||
Key: &rsa.PublicKey{},
|
|
||||||
}, &jose.JsonWebSignature{
|
|
||||||
Signatures: []jose.Signature{
|
|
||||||
jose.Signature{
|
|
||||||
Header: jose.JoseHeader{
|
|
||||||
Algorithm: "HS256",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
expected := "algorithm 'HS256' in JWS header not acceptable"
|
|
||||||
if err == nil || err.Error() != expected {
|
|
||||||
t.Errorf("HS256 JWS: Expected error '%s', got '%s'", expected, err)
|
|
||||||
}
|
|
||||||
if stat != "InvalidJWSAlgorithm" {
|
|
||||||
t.Errorf("HS256 JWS: Expected stat '%s', got '%s'", "InvalidJWSAlgorithm", stat)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCheckAlgorithmBadJWK(t *testing.T) {
|
|
||||||
stat, err := checkAlgorithm(&jose.JsonWebKey{
|
|
||||||
Algorithm: "HS256",
|
|
||||||
Key: &rsa.PublicKey{},
|
|
||||||
}, &jose.JsonWebSignature{
|
|
||||||
Signatures: []jose.Signature{
|
|
||||||
jose.Signature{
|
|
||||||
Header: jose.JoseHeader{
|
|
||||||
Algorithm: "RS256",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
expected := "algorithm 'HS256' on JWK is unacceptable"
|
|
||||||
if err == nil || err.Error() != expected {
|
|
||||||
t.Errorf("HS256 JWS: Expected error '%s', got '%s'", expected, err)
|
|
||||||
}
|
|
||||||
if stat != "InvalidAlgorithmOnKey" {
|
|
||||||
t.Errorf("HS256 JWS: Expected stat '%s', got '%s'", "InvalidAlgorithmOnKey", stat)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCheckAlgorithmSuccess(t *testing.T) {
|
|
||||||
_, err := checkAlgorithm(&jose.JsonWebKey{
|
|
||||||
Algorithm: "RS256",
|
|
||||||
Key: &rsa.PublicKey{},
|
|
||||||
}, &jose.JsonWebSignature{
|
|
||||||
Signatures: []jose.Signature{
|
|
||||||
jose.Signature{
|
|
||||||
Header: jose.JoseHeader{
|
|
||||||
Algorithm: "RS256",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("RS256 key: Expected nil error, got '%s'", err)
|
|
||||||
}
|
|
||||||
_, err = checkAlgorithm(&jose.JsonWebKey{
|
|
||||||
Key: &rsa.PublicKey{},
|
|
||||||
}, &jose.JsonWebSignature{
|
|
||||||
Signatures: []jose.Signature{
|
|
||||||
jose.Signature{
|
|
||||||
Header: jose.JoseHeader{
|
|
||||||
Algorithm: "RS256",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("RS256 key: Expected nil error, got '%s'", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBadKeyCSR(t *testing.T) {
|
func TestBadKeyCSR(t *testing.T) {
|
||||||
wfe, _ := setupWFE(t)
|
wfe, _ := setupWFE(t)
|
||||||
responseWriter := httptest.NewRecorder()
|
responseWriter := httptest.NewRecorder()
|
||||||
|
|
Loading…
Reference in New Issue