def action_signatures_are_correct(app_configs, **kwargs): errors = [] try: Action = apps.get_model("recipes", "Action") # pre-fetch signatures, to avoid race condition with deleted signatures signed_actions = list( Action.objects.exclude(signature=None).select_related("signature")) except (ProgrammingError, OperationalError, ImproperlyConfigured) as e: msg = f"Could not retrieve actions: f{e}" errors.append(Info(msg, id=INFO_COULD_NOT_RETRIEVE_ACTIONS)) return errors try: for action in signed_actions: data = action.canonical_json() signature = action.signature.signature pubkey = action.signature.public_key x5u = action.signature.x5u try: if x5u: signing.verify_signature_x5u(data, signature, x5u) else: signing.verify_signature_pubkey(data, signature, pubkey) except signing.BadSignature as e: msg = f"Action '{action}' (id={action.id}) has a bad signature: {e.detail}" errors.append(Error(msg, id=ERROR_INVALID_ACTION_SIGNATURE)) except (ProgrammingError, OperationalError, ImproperlyConfigured) as e: errors.append( Warning(f"Could not check signatures: {e}", id=WARNING_COULD_NOT_CHECK_SIGNATURES)) return errors
def recipe_signatures_are_correct(app_configs, **kwargs): errors = [] try: Recipe = apps.get_model("recipes", "Recipe") # pre-fetch signatures, to avoid race condition with deleted signatures signed_recipes = list( Recipe.objects.exclude(signature=None).select_related("signature")) except (ProgrammingError, OperationalError, ImproperlyConfigured) as e: errors.append( Info(f"Could not retrieve recipes: {e}", id=INFO_COULD_NOT_RETRIEVE_RECIPES)) return errors try: for recipe in signed_recipes: data = recipe.canonical_json() signature = recipe.signature.signature pubkey = recipe.signature.public_key x5u = recipe.signature.x5u try: if x5u: signing.verify_signature_x5u(data, signature, x5u) else: signing.verify_signature_pubkey(data, signature, pubkey) except signing.BadSignature as e: msg = "Recipe '{recipe}' (id={recipe.id}) has a bad signature: {detail}".format( recipe=recipe, detail=e.detail) errors.append(Error(msg, id=ERROR_INVALID_RECIPE_SIGNATURE)) except (ProgrammingError, OperationalError, ImproperlyConfigured) as e: errors.append( Warning(f"Could not check signatures: {e}", id=WARNING_COULD_NOT_CHECK_SIGNATURES)) return errors
def test_happy_path(self, mocker): mock_verify_x5u = mocker.patch("normandy.recipes.signing.verify_x5u") mock_der_encode = mocker.patch("normandy.recipes.signing.der_encode") mock_verify_signature_pubkey = mocker.patch( "normandy.recipes.signing.verify_signature_pubkey" ) data = "abc" signature = "signature" x5u = "http://example.com/cert" cert_contents = b"cert_contents" encoded_cert_contents = base64.b64encode(cert_contents).decode() mock_der_encode.return_value = cert_contents public_key = "public_key" cert = {"tbsCertificate": {"subjectPublicKeyInfo": public_key}} mock_verify_x5u.return_value = cert ret = signing.verify_signature_x5u(data, signature, x5u) mock_verify_x5u.assert_called_with(x5u) mock_der_encode.assert_called_with(public_key) mock_verify_signature_pubkey.assert_called_with(data, signature, encoded_cert_contents) assert ret == mock_verify_signature_pubkey.return_value
def test_action_signatures(conf, requests_session): r = requests_session.get( conf.getoption("server") + "/api/v1/action/signed/") r.raise_for_status() data = r.json() if len(data) == 0: pytest.skip("No signed actions") for item in data: canonical_action = canonical_json(item["action"]) signature = item["signature"]["signature"] x5u = item["signature"]["x5u"] assert signing.verify_signature_x5u(canonical_action, signature, x5u)