Repository: pyca/cryptography

Issue:
Add more reason tags for UnsupportedAlgorithm
There are a fair few places we raise untagged UnsupportedAlgorithm exceptions. I think most of these should probably get new reason codes and updated tests?



Discussion:
Turns out there are actually only 2.
- [ ] MGF1
- [ ] Asym stuff in multibackend


Resolving patch:
diff --git a/cryptography/exceptions.py b/cryptography/exceptions.py
--- a/cryptography/exceptions.py
+++ b/cryptography/exceptions.py
@@ -19,6 +19,8 @@ class _Reasons(object):
     UNSUPPORTED_HASH = object()
     UNSUPPORTED_CIPHER = object()
     UNSUPPORTED_PADDING = object()
+    UNSUPPORTED_MGF = object()
+    UNSUPPORTED_PUBLIC_KEY_ALGORITHM = object()
 
 
 class UnsupportedAlgorithm(Exception):
diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py
--- a/cryptography/hazmat/backends/multibackend.py
+++ b/cryptography/hazmat/backends/multibackend.py
@@ -126,16 +126,19 @@ def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
     def generate_rsa_private_key(self, public_exponent, key_size):
         for b in self._filtered_backends(RSABackend):
             return b.generate_rsa_private_key(public_exponent, key_size)
-        raise UnsupportedAlgorithm("RSA is not supported by the backend")
+        raise UnsupportedAlgorithm("RSA is not supported by the backend",
+                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
 
     def create_rsa_signature_ctx(self, private_key, padding, algorithm):
         for b in self._filtered_backends(RSABackend):
             return b.create_rsa_signature_ctx(private_key, padding, algorithm)
-        raise UnsupportedAlgorithm("RSA is not supported by the backend")
+        raise UnsupportedAlgorithm("RSA is not supported by the backend",
+                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
 
     def create_rsa_verification_ctx(self, public_key, signature, padding,
                                     algorithm):
         for b in self._filtered_backends(RSABackend):
             return b.create_rsa_verification_ctx(public_key, signature,
                                                  padding, algorithm)
-        raise UnsupportedAlgorithm("RSA is not supported by the backend")
+        raise UnsupportedAlgorithm("RSA is not supported by the backend",
+                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -731,7 +731,8 @@ def __init__(self, backend, private_key, padding, algorithm):
         elif isinstance(padding, PSS):
             if not isinstance(padding._mgf, MGF1):
                 raise UnsupportedAlgorithm(
-                    "Only MGF1 is supported by this backend"
+                    "Only MGF1 is supported by this backend",
+                    _Reasons.UNSUPPORTED_MGF
                 )
 
             # Size of key in bytes - 2 is the maximum
@@ -915,7 +916,8 @@ def __init__(self, backend, public_key, signature, padding, algorithm):
         elif isinstance(padding, PSS):
             if not isinstance(padding._mgf, MGF1):
                 raise UnsupportedAlgorithm(
-                    "Only MGF1 is supported by this backend"
+                    "Only MGF1 is supported by this backend",
+                    _Reasons.UNSUPPORTED_MGF
                 )
 
             # Size of key in bytes - 2 is the maximum


Test patch:
diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -13,8 +13,6 @@
 
 from __future__ import absolute_import, division, print_function
 
-import pytest
-
 from cryptography import utils
 from cryptography.exceptions import (
     UnsupportedAlgorithm, _Reasons
@@ -179,13 +177,19 @@ def test_rsa(self):
                                             padding.PKCS1v15(), hashes.MD5())
 
         backend = MultiBackend([])
-        with pytest.raises(UnsupportedAlgorithm):
+        with raises_unsupported_algorithm(
+            _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+        ):
             backend.generate_rsa_private_key(key_size=1024, public_exponent=3)
 
-        with pytest.raises(UnsupportedAlgorithm):
+        with raises_unsupported_algorithm(
+            _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+        ):
             backend.create_rsa_signature_ctx("private_key", padding.PKCS1v15(),
                                              hashes.MD5())
 
-        with pytest.raises(UnsupportedAlgorithm):
+        with raises_unsupported_algorithm(
+            _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+        ):
             backend.create_rsa_verification_ctx(
                 "public_key", "sig", padding.PKCS1v15(), hashes.MD5())
diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py
--- a/tests/hazmat/primitives/test_rsa.py
+++ b/tests/hazmat/primitives/test_rsa.py
@@ -22,7 +22,7 @@
 import pytest
 
 from cryptography import exceptions, utils
-from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+from cryptography.exceptions import _Reasons
 from cryptography.hazmat.primitives import hashes, interfaces
 from cryptography.hazmat.primitives.asymmetric import padding, rsa
 
@@ -630,7 +630,7 @@ def test_unsupported_pss_mgf(self, backend):
             key_size=512,
             backend=backend
         )
-        with pytest.raises(UnsupportedAlgorithm):
+        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF):
             private_key.signer(padding.PSS(mgf=DummyMGF()), hashes.SHA1(),
                                backend)
 
@@ -881,7 +881,7 @@ def test_unsupported_pss_mgf(self, backend):
             backend=backend
         )
         public_key = private_key.public_key()
-        with pytest.raises(UnsupportedAlgorithm):
+        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF):
             public_key.verifier(b"sig", padding.PSS(mgf=DummyMGF()),
                                 hashes.SHA1(), backend)