Ejemplo n.º 1
0
// Unwraps a RPCError and returns the correct error type.
func unwrapError(rpcError RPCError) (err error) {
	if rpcError.Value != "" {
		switch rpcError.Type {
		case "InternalServerError":
			err = core.InternalServerError(rpcError.Value)
		case "NotSupportedError":
			err = core.NotSupportedError(rpcError.Value)
		case "MalformedRequestError":
			err = core.MalformedRequestError(rpcError.Value)
		case "UnauthorizedError":
			err = core.UnauthorizedError(rpcError.Value)
		case "NotFoundError":
			err = core.NotFoundError(rpcError.Value)
		case "SyntaxError":
			err = core.SyntaxError(rpcError.Value)
		case "SignatureValidationError":
			err = core.SignatureValidationError(rpcError.Value)
		case "CertificateIssuanceError":
			err = core.CertificateIssuanceError(rpcError.Value)
		default:
			err = errors.New(rpcError.Value)
		}
	}
	return
}
Ejemplo n.º 2
0
func unwrapError(err error) error {
	code := grpc.Code(err)
	errBody := grpc.ErrorDesc(err)
	switch code {
	case InternalServerError:
		return core.InternalServerError(errBody)
	case NotSupportedError:
		return core.NotSupportedError(errBody)
	case MalformedRequestError:
		return core.MalformedRequestError(errBody)
	case UnauthorizedError:
		return core.UnauthorizedError(errBody)
	case NotFoundError:
		return core.NotFoundError(errBody)
	case SignatureValidationError:
		return core.SignatureValidationError(errBody)
	case NoSuchRegistrationError:
		return core.NoSuchRegistrationError(errBody)
	case RateLimitedError:
		return core.RateLimitedError(errBody)
	case LengthRequiredError:
		return core.LengthRequiredError(errBody)
	case BadNonceError:
		return core.BadNonceError(errBody)
	default:
		return err
	}
}
Ejemplo n.º 3
0
// Unwraps a rpcError and returns the correct error type.
func unwrapError(rpcError *rpcError) error {
	if rpcError != nil {
		switch rpcError.Type {
		case "InternalServerError":
			return core.InternalServerError(rpcError.Value)
		case "NotSupportedError":
			return core.NotSupportedError(rpcError.Value)
		case "MalformedRequestError":
			return core.MalformedRequestError(rpcError.Value)
		case "UnauthorizedError":
			return core.UnauthorizedError(rpcError.Value)
		case "NotFoundError":
			return core.NotFoundError(rpcError.Value)
		case "SignatureValidationError":
			return core.SignatureValidationError(rpcError.Value)
		case "NoSuchRegistrationError":
			return core.NoSuchRegistrationError(rpcError.Value)
		case "TooManyRPCRequestsError":
			return core.TooManyRPCRequestsError(rpcError.Value)
		case "RateLimitedError":
			return core.RateLimitedError(rpcError.Value)
		default:
			if strings.HasPrefix(rpcError.Type, "urn:") {
				return &probs.ProblemDetails{
					Type:       probs.ProblemType(rpcError.Type),
					Detail:     rpcError.Value,
					HTTPStatus: rpcError.HTTPStatus,
				}
			}
			return errors.New(rpcError.Value)
		}
	}
	return nil
}
Ejemplo n.º 4
0
// Unwraps a rpcError and returns the correct error type.
func unwrapError(rpcError *rpcError) error {
	if rpcError != nil {
		switch rpcError.Type {
		case "InternalServerError":
			return core.InternalServerError(rpcError.Value)
		case "NotSupportedError":
			return core.NotSupportedError(rpcError.Value)
		case "MalformedRequestError":
			return core.MalformedRequestError(rpcError.Value)
		case "UnauthorizedError":
			return core.UnauthorizedError(rpcError.Value)
		case "NotFoundError":
			return core.NotFoundError(rpcError.Value)
		case "SyntaxError":
			return core.SyntaxError(rpcError.Value)
		case "SignatureValidationError":
			return core.SignatureValidationError(rpcError.Value)
		case "CertificateIssuanceError":
			return core.CertificateIssuanceError(rpcError.Value)
		case "NoSuchRegistrationError":
			return core.NoSuchRegistrationError(rpcError.Value)
		case "TooManyRPCRequestsError":
			return core.TooManyRPCRequestsError(rpcError.Value)
		case "RateLimitedError":
			return core.RateLimitedError(rpcError.Value)
		case "ServiceUnavailableError":
			return core.ServiceUnavailableError(rpcError.Value)
		default:
			return errors.New(rpcError.Value)
		}
	}
	return nil
}
Ejemplo n.º 5
0
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")
}
Ejemplo n.º 6
0
// 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
}
Ejemplo n.º 7
0
func TestWrapError(t *testing.T) {
	testCases := []error{
		core.InternalServerError("foo"),
		core.NotSupportedError("foo"),
		core.MalformedRequestError("foo"),
		core.UnauthorizedError("foo"),
		core.NotFoundError("foo"),
		core.SignatureValidationError("foo"),
		core.CertificateIssuanceError("foo"),
		core.NoSuchRegistrationError("foo"),
		core.RateLimitedError("foo"),
		core.TooManyRPCRequestsError("foo"),
		errors.New("foo"),
	}
	for _, c := range testCases {
		wrapped := wrapError(c)
		test.AssertEquals(t, wrapped.Type, reflect.TypeOf(c).Name())
		test.AssertEquals(t, wrapped.Value, "foo")
		unwrapped := unwrapError(wrapped)
		test.AssertEquals(t, wrapped.Type, reflect.TypeOf(unwrapped).Name())
		test.AssertEquals(t, unwrapped.Error(), "foo")
	}

	complicated := []struct {
		given    error
		expected error
	}{
		{
			&probs.ProblemDetails{
				Type:       probs.ConnectionProblem,
				Detail:     "whoops",
				HTTPStatus: 417,
			},
			&probs.ProblemDetails{
				Type:       probs.ConnectionProblem,
				Detail:     "whoops",
				HTTPStatus: 417,
			},
		},
		{
			&probs.ProblemDetails{Type: "invalid", Detail: "hm"},
			errors.New("hm"),
		},
		{
			errors.New(""),
			errors.New(""),
		},
	}
	for i, tc := range complicated {
		actual := unwrapError(wrapError(tc.given))
		if !reflect.DeepEqual(tc.expected, actual) {
			t.Errorf("rpc error wrapping case %d: want %#v, got %#v", i, tc.expected, actual)
		}

	}
}
Ejemplo n.º 8
0
func algorithmForKey(key *jose.JsonWebKey) (string, error) {
	switch k := key.Key.(type) {
	case *rsa.PublicKey:
		return string(jose.RS256), nil
	case *ecdsa.PublicKey:
		switch k.Params().Name {
		case "P-256":
			return string(jose.ES256), nil
		case "P-384":
			return string(jose.ES384), nil
		case "P-521":
			return string(jose.ES512), nil
		}
	}
	return "", core.SignatureValidationError("no signature algorithms suitable for given key type")
}
Ejemplo n.º 9
0
func TestWrapError(t *testing.T) {
	testCases := []error{
		core.InternalServerError("foo"),
		core.NotSupportedError("foo"),
		core.MalformedRequestError("foo"),
		core.UnauthorizedError("foo"),
		core.NotFoundError("foo"),
		core.SyntaxError("foo"),
		core.SignatureValidationError("foo"),
		core.CertificateIssuanceError("foo"),
		core.NoSuchRegistrationError("foo"),
		core.RateLimitedError("foo"),
		core.TooManyRPCRequestsError("foo"),
	}
	for _, c := range testCases {
		wrapped := wrapError(c)
		test.AssertEquals(t, wrapped.Type, reflect.TypeOf(c).Name())
		test.AssertEquals(t, wrapped.Value, "foo")
		unwrapped := unwrapError(wrapped)
		test.AssertEquals(t, wrapped.Type, reflect.TypeOf(unwrapped).Name())
		test.AssertEquals(t, unwrapped.Error(), "foo")
	}
}
Ejemplo n.º 10
0
func TestErrors(t *testing.T) {
	testcases := []struct {
		err          error
		expectedCode codes.Code
	}{
		{core.MalformedRequestError("test 1"), MalformedRequestError},
		{core.NotSupportedError("test 2"), NotSupportedError},
		{core.UnauthorizedError("test 3"), UnauthorizedError},
		{core.NotFoundError("test 4"), NotFoundError},
		{core.LengthRequiredError("test 5"), LengthRequiredError},
		{core.SignatureValidationError("test 6"), SignatureValidationError},
		{core.RateLimitedError("test 7"), RateLimitedError},
		{core.BadNonceError("test 8"), BadNonceError},
		{core.NoSuchRegistrationError("test 9"), NoSuchRegistrationError},
		{core.InternalServerError("test 10"), InternalServerError},
	}

	for _, tc := range testcases {
		wrappedErr := wrapError(tc.err)
		test.AssertEquals(t, grpc.Code(wrappedErr), tc.expectedCode)
		test.AssertEquals(t, tc.err, unwrapError(wrappedErr))
	}
}
Ejemplo n.º 11
0
func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool, resource core.AcmeResource) ([]byte, *jose.JsonWebKey, core.Registration, error) {
	var err error
	var reg core.Registration

	// Read body
	if request.Body == nil {
		err = core.MalformedRequestError("No body on POST")
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	}

	bodyBytes, err := ioutil.ReadAll(request.Body)
	if err != nil {
		err = core.InternalServerError(err.Error())
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	}

	body := string(bodyBytes)
	// Parse as JWS
	parsedJws, err := jose.ParseSigned(body)
	if err != nil {
		puberr := core.SignatureValidationError("Parse error reading JWS")
		wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error()))
		return nil, nil, reg, puberr
	}

	// Verify JWS
	// NOTE: It might seem insecure for the WFE to be trusted to verify
	// client requests, i.e., that the verification should be done at the
	// RA.  However the WFE is the RA's only view of the outside world
	// *anyway*, so it could always lie about what key was used by faking
	// the signature itself.
	if len(parsedJws.Signatures) > 1 {
		err = core.SignatureValidationError("Too many signatures on POST")
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	}
	if len(parsedJws.Signatures) == 0 {
		err = core.SignatureValidationError("POST JWS not signed")
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	}
	key := parsedJws.Signatures[0].Header.JsonWebKey
	payload, header, err := parsedJws.Verify(key)
	if err != nil {
		puberr := core.SignatureValidationError("JWS verification error")
		wfe.log.Debug(string(body))
		wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error()))
		return nil, nil, reg, puberr
	}

	// Check that the request has a known anti-replay nonce
	// i.e., Nonce is in protected header and
	if err != nil || len(header.Nonce) == 0 {
		err = core.SignatureValidationError("JWS has no anti-replay nonce")
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	} else if !wfe.nonceService.Valid(header.Nonce) {
		err = core.SignatureValidationError(fmt.Sprintf("JWS has invalid anti-replay nonce"))
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	}

	reg, err = wfe.SA.GetRegistrationByKey(*key)
	if err != nil {
		// If we are requiring a valid registration, any failure to look up the
		// registration is an overall failure to verify.
		if regCheck {
			return nil, nil, reg, err
		}
		// Otherwise we just return an empty registration. The caller is expected
		// to use the returned key instead.
		reg = core.Registration{}
	}

	// Check that the "resource" field is present and has the correct value
	var parsedRequest struct {
		Resource string `json:"resource"`
	}
	err = json.Unmarshal([]byte(payload), &parsedRequest)
	if err != nil {
		puberr := core.SignatureValidationError("Request payload did not parse as JSON")
		wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error()))
		return nil, nil, reg, puberr
	}
	if parsedRequest.Resource == "" {
		err = core.MalformedRequestError("Request payload does not specify a resource")
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	} else if resource != core.AcmeResource(parsedRequest.Resource) {
		err = core.MalformedRequestError(fmt.Sprintf("Request payload has invalid resource: %s != %s", parsedRequest.Resource, resource))
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	}

	return []byte(payload), key, reg, nil
}
Ejemplo n.º 12
0
// verifyPOST reads and parses the request body, looks up the Registration
// corresponding to its JWK, verifies the JWS signature, checks that the
// resource field is present and correct in the JWS protected header, and
// returns the JWS payload bytes, the key used to verify, and the corresponding
// Registration (or error).  If regCheck is false, verifyPOST will still try to
// look up a registration object, and will return it if found. However, if no
// registration object is found, verifyPOST will attempt to verify the JWS using
// the key in the JWS headers, and return the key plus a dummy registration if
// successful. If a caller passes regCheck = false, it should plan on validating
// the key itself.  verifyPOST also appends its errors to requestEvent.Errors so
// code calling it does not need to if they imediately return a response to the
// user.
func (wfe *WebFrontEndImpl) verifyPOST(logEvent *requestEvent, request *http.Request, regCheck bool, resource core.AcmeResource) ([]byte, *jose.JsonWebKey, core.Registration, error) {
	var err error
	// TODO: We should return a pointer to a registration, which can be nil,
	// rather the a registration value with a sentinel value.
	// https://github.com/letsencrypt/boulder/issues/877
	reg := core.Registration{ID: 0}

	if _, ok := request.Header["Content-Length"]; !ok {
		err = core.LengthRequiredError("Content-Length header is required for POST.")
		wfe.stats.Inc("WFE.HTTP.ClientErrors.LengthRequiredError", 1, 1.0)
		logEvent.AddError("missing Content-Length header on POST")
		return nil, nil, reg, err
	}

	// Read body
	if request.Body == nil {
		err = core.MalformedRequestError("No body on POST")
		wfe.stats.Inc("WFE.Errors.NoPOSTBody", 1, 1.0)
		logEvent.AddError("no body on POST")
		return nil, nil, reg, err
	}

	bodyBytes, err := ioutil.ReadAll(request.Body)
	if err != nil {
		err = core.InternalServerError("unable to read request body")
		wfe.stats.Inc("WFE.Errors.UnableToReadRequestBody", 1, 1.0)
		logEvent.AddError("unable to read request body")
		return nil, nil, reg, err
	}

	body := string(bodyBytes)
	// Parse as JWS
	parsedJws, err := jose.ParseSigned(body)
	if err != nil {
		puberr := core.SignatureValidationError("Parse error reading JWS")
		wfe.stats.Inc("WFE.Errors.UnableToParseJWS", 1, 1.0)
		logEvent.AddError("could not JSON parse body into JWS: %s", err)
		return nil, nil, reg, puberr
	}

	// Verify JWS
	// NOTE: It might seem insecure for the WFE to be trusted to verify
	// client requests, i.e., that the verification should be done at the
	// RA.  However the WFE is the RA's only view of the outside world
	// *anyway*, so it could always lie about what key was used by faking
	// the signature itself.
	if len(parsedJws.Signatures) > 1 {
		err = core.SignatureValidationError("Too many signatures in POST body")
		wfe.stats.Inc("WFE.Errors.TooManyJWSSignaturesInPOST", 1, 1.0)
		logEvent.AddError("too many signatures in POST body: %d", len(parsedJws.Signatures))
		return nil, nil, reg, err
	}
	if len(parsedJws.Signatures) == 0 {
		err = core.SignatureValidationError("POST JWS not signed")
		wfe.stats.Inc("WFE.Errors.JWSNotSignedInPOST", 1, 1.0)
		logEvent.AddError("no signatures in POST body")
		return nil, nil, reg, err
	}
	submittedKey := parsedJws.Signatures[0].Header.JsonWebKey
	if submittedKey == nil {
		err = core.SignatureValidationError("No JWK in JWS header")
		wfe.stats.Inc("WFE.Errors.NoJWKInJWSSignatureHeader", 1, 1.0)
		logEvent.AddError("no JWK in JWS signature header in POST body")
		return nil, nil, reg, err
	}

	var key *jose.JsonWebKey
	reg, err = wfe.SA.GetRegistrationByKey(*submittedKey)
	// Special case: If no registration was found, but regCheck is false, use an
	// empty registration and the submitted key. The caller is expected to do some
	// validation on the returned key.
	if _, ok := err.(core.NoSuchRegistrationError); ok && !regCheck {
		// When looking up keys from the registrations DB, we can be confident they
		// are "good". But when we are verifying against any submitted key, we want
		// to check its quality before doing the verify.
		if err = core.GoodKey(submittedKey.Key); err != nil {
			wfe.stats.Inc("WFE.Errors.JWKRejectedByGoodKey", 1, 1.0)
			logEvent.AddError("JWK in request was rejected by GoodKey: %s", err)
			return nil, nil, reg, err
		}
		key = submittedKey
	} else if err != nil {
		// For all other errors, or if regCheck is true, return error immediately.
		wfe.stats.Inc("WFE.Errors.UnableToGetRegistrationByKey", 1, 1.0)
		logEvent.AddError("unable to fetch registration by the given JWK: %s", err)
		return nil, nil, reg, err
	} else {
		// If the lookup was successful, use that key.
		key = &reg.Key
		logEvent.Requester = reg.ID
		logEvent.Contacts = reg.Contact
	}

	payload, header, err := parsedJws.Verify(key)
	if err != nil {
		puberr := core.SignatureValidationError("JWS verification error")
		wfe.stats.Inc("WFE.Errors.JWSVerificationFailed", 1, 1.0)
		n := len(body)
		if n > 100 {
			n = 100
		}
		logEvent.AddError("verification of JWS with the JWK failed: %v; body: %s", err, body[:n])
		return nil, nil, reg, puberr
	}

	// Check that the request has a known anti-replay nonce
	// i.e., Nonce is in protected header and
	if err != nil || len(header.Nonce) == 0 {
		wfe.stats.Inc("WFE.Errors.JWSMissingNonce", 1, 1.0)
		logEvent.AddError("JWS is missing an anti-replay nonce")
		err = core.SignatureValidationError("JWS has no anti-replay nonce")
		return nil, nil, reg, err
	} else if !wfe.nonceService.Valid(header.Nonce) {
		wfe.stats.Inc("WFE.Errors.JWSInvalidNonce", 1, 1.0)
		logEvent.AddError("JWS has an invalid anti-replay nonce")
		err = core.SignatureValidationError(fmt.Sprintf("JWS has invalid anti-replay nonce"))
		return nil, nil, reg, err
	}

	// Check that the "resource" field is present and has the correct value
	var parsedRequest struct {
		Resource string `json:"resource"`
	}
	err = json.Unmarshal([]byte(payload), &parsedRequest)
	if err != nil {
		wfe.stats.Inc("WFE.Errors.UnparsableJWSPayload", 1, 1.0)
		logEvent.AddError("unable to JSON parse resource from JWS payload: %s", err)
		puberr := core.SignatureValidationError("Request payload did not parse as JSON")
		return nil, nil, reg, puberr
	}
	if parsedRequest.Resource == "" {
		wfe.stats.Inc("WFE.Errors.NoResourceInJWSPayload", 1, 1.0)
		logEvent.AddError("JWS request payload does not specifiy a resource")
		err = core.MalformedRequestError("Request payload does not specify a resource")
		return nil, nil, reg, err
	} else if resource != core.AcmeResource(parsedRequest.Resource) {
		wfe.stats.Inc("WFE.Errors.MismatchedResourceInJWSPayload", 1, 1.0)
		logEvent.AddError("JWS request payload does not match resource")
		err = core.MalformedRequestError(fmt.Sprintf("JWS resource payload does not match the HTTP resource: %s != %s", parsedRequest.Resource, resource))
		return nil, nil, reg, err
	}

	return []byte(payload), key, reg, nil
}
Ejemplo n.º 13
0
// verifyPOST reads and parses the request body, looks up the Registration
// corresponding to its JWK, verifies the JWS signature,
// checks that the resource field is present and correct in the JWS protected
// header, and returns the JWS payload bytes, the key used to verify, and the
// corresponding Registration (or error).
// If regCheck is false, verifyPOST will still try to look up a registration
// object, and will return it if found. However, if no registration object is
// found, verifyPOST will attempt to verify the JWS using the key in the JWS
// headers, and return the key plus a dummy registration if successful. If a
// caller passes regCheck = false, it should plan on validating the key itself.
func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool, resource core.AcmeResource) ([]byte, *jose.JsonWebKey, core.Registration, error) {
	var err error
	// TODO: We should return a pointer to a registration, which can be nil,
	// rather the a registration value with a sentinel value.
	// https://github.com/letsencrypt/boulder/issues/877
	reg := core.Registration{ID: -1}

	if _, ok := request.Header["Content-Length"]; !ok {
		err = core.LengthRequiredError("Content-Length header is required for POST.")
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	}

	// Read body
	if request.Body == nil {
		err = core.MalformedRequestError("No body on POST")
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	}

	bodyBytes, err := ioutil.ReadAll(request.Body)
	if err != nil {
		err = core.InternalServerError(err.Error())
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	}

	body := string(bodyBytes)
	// Parse as JWS
	parsedJws, err := jose.ParseSigned(body)
	if err != nil {
		puberr := core.SignatureValidationError("Parse error reading JWS")
		wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error()))
		return nil, nil, reg, puberr
	}

	// Verify JWS
	// NOTE: It might seem insecure for the WFE to be trusted to verify
	// client requests, i.e., that the verification should be done at the
	// RA.  However the WFE is the RA's only view of the outside world
	// *anyway*, so it could always lie about what key was used by faking
	// the signature itself.
	if len(parsedJws.Signatures) > 1 {
		err = core.SignatureValidationError("Too many signatures on POST")
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	}
	if len(parsedJws.Signatures) == 0 {
		err = core.SignatureValidationError("POST JWS not signed")
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	}
	submittedKey := parsedJws.Signatures[0].Header.JsonWebKey
	if submittedKey == nil {
		err = core.SignatureValidationError("No JWK in JWS header")
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	}

	var key *jose.JsonWebKey
	reg, err = wfe.SA.GetRegistrationByKey(*submittedKey)
	// Special case: If no registration was found, but regCheck is false, use an
	// empty registration and the submitted key. The caller is expected to do some
	// validation on the returned key.
	if _, ok := err.(core.NoSuchRegistrationError); ok && !regCheck {
		// When looking up keys from the registrations DB, we can be confident they
		// are "good". But when we are verifying against any submitted key, we want
		// to check its quality before doing the verify.
		if err = core.GoodKey(submittedKey.Key); err != nil {
			return nil, nil, reg, err
		}
		key = submittedKey
	} else if err != nil {
		// For all other errors, or if regCheck is true, return error immediately.
		return nil, nil, reg, err
	} else {
		// If the lookup was successful, use that key.
		key = &reg.Key
	}

	payload, header, err := parsedJws.Verify(key)
	if err != nil {
		puberr := core.SignatureValidationError("JWS verification error")
		wfe.log.Debug(string(body))
		wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error()))
		return nil, nil, reg, puberr
	}

	// Check that the request has a known anti-replay nonce
	// i.e., Nonce is in protected header and
	if err != nil || len(header.Nonce) == 0 {
		err = core.SignatureValidationError("JWS has no anti-replay nonce")
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	} else if !wfe.nonceService.Valid(header.Nonce) {
		err = core.SignatureValidationError(fmt.Sprintf("JWS has invalid anti-replay nonce"))
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	}

	// Check that the "resource" field is present and has the correct value
	var parsedRequest struct {
		Resource string `json:"resource"`
	}
	err = json.Unmarshal([]byte(payload), &parsedRequest)
	if err != nil {
		puberr := core.SignatureValidationError("Request payload did not parse as JSON")
		wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error()))
		return nil, nil, reg, puberr
	}
	if parsedRequest.Resource == "" {
		err = core.MalformedRequestError("Request payload does not specify a resource")
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	} else if resource != core.AcmeResource(parsedRequest.Resource) {
		err = core.MalformedRequestError(fmt.Sprintf("Request payload has invalid resource: %s != %s", parsedRequest.Resource, resource))
		wfe.log.Debug(err.Error())
		return nil, nil, reg, err
	}

	return []byte(payload), key, reg, nil
}