Merge pull request #1115 from reaperhulk/rsa-numbers-serialization

RSA Keys -> Numbers Interface
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 13c62de..a38534d 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -18,7 +18,7 @@
   and
   :class:`~cryptography.hazmat.backends.interfaces.TraditionalOpenSSLSerializationBackend`
   support to the :doc:`/hazmat/backends/openssl`.
-
+* Added :class:`~cryptography.hazmat.backends.interfaces.EllipticCurveBackend`.
 
 0.4 - 2014-05-03
 ~~~~~~~~~~~~~~~~
diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py
index f3c7937..27ab063 100644
--- a/cryptography/hazmat/backends/multibackend.py
+++ b/cryptography/hazmat/backends/multibackend.py
@@ -16,8 +16,8 @@
 from cryptography import utils
 from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
 from cryptography.hazmat.backends.interfaces import (
-    CMACBackend, CipherBackend, DSABackend, HMACBackend, HashBackend,
-    PBKDF2HMACBackend, RSABackend
+    CMACBackend, CipherBackend, DSABackend, EllipticCurveBackend, HMACBackend,
+    HashBackend, PBKDF2HMACBackend, RSABackend
 )
 
 
@@ -28,6 +28,7 @@
 @utils.register_interface(PBKDF2HMACBackend)
 @utils.register_interface(RSABackend)
 @utils.register_interface(DSABackend)
+@utils.register_interface(EllipticCurveBackend)
 class MultiBackend(object):
     name = "multibackend"
 
@@ -243,3 +244,55 @@
                 pass
         raise UnsupportedAlgorithm("This backend does not support CMAC.",
                                    _Reasons.UNSUPPORTED_CIPHER)
+
+    def elliptic_curve_supported(self, curve):
+        return any(
+            b.elliptic_curve_supported(curve)
+            for b in self._filtered_backends(EllipticCurveBackend)
+        )
+
+    def elliptic_curve_signature_algorithm_supported(
+        self, signature_algorithm, curve
+    ):
+        return any(
+            b.elliptic_curve_signature_algorithm_supported(
+                signature_algorithm, curve
+            )
+            for b in self._filtered_backends(EllipticCurveBackend)
+        )
+
+    def generate_elliptic_curve_private_key(self, curve):
+        for b in self._filtered_backends(EllipticCurveBackend):
+            try:
+                return b.generate_elliptic_curve_private_key(curve)
+            except UnsupportedAlgorithm:
+                continue
+
+        raise UnsupportedAlgorithm(
+            "This backend does not support this elliptic curve.",
+            _Reasons.UNSUPPORTED_ELLIPTIC_CURVE
+        )
+
+    def elliptic_curve_private_key_from_numbers(self, numbers):
+        for b in self._filtered_backends(EllipticCurveBackend):
+            try:
+                return b.elliptic_curve_private_key_from_numbers(numbers)
+            except UnsupportedAlgorithm:
+                continue
+
+        raise UnsupportedAlgorithm(
+            "This backend does not support this elliptic curve.",
+            _Reasons.UNSUPPORTED_ELLIPTIC_CURVE
+        )
+
+    def elliptic_curve_public_key_from_numbers(self, numbers):
+        for b in self._filtered_backends(EllipticCurveBackend):
+            try:
+                return b.elliptic_curve_public_key_from_numbers(numbers)
+            except UnsupportedAlgorithm:
+                continue
+
+        raise UnsupportedAlgorithm(
+            "This backend does not support this elliptic curve.",
+            _Reasons.UNSUPPORTED_ELLIPTIC_CURVE
+        )
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index 9cf92f9..ab3f947 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -905,7 +905,7 @@
             return False
 
         # We only support ECDSA right now.
-        if isinstance(signature_algorithm, ec.ECDSA) is False:
+        if not isinstance(signature_algorithm, ec.ECDSA):
             return False
 
         # Before 0.9.8m OpenSSL can't cope with digests longer than the curve.
@@ -915,10 +915,7 @@
         ):
             return False
 
-        if not self.elliptic_curve_supported(curve):
-            return False
-        else:
-            return True
+        return self.elliptic_curve_supported(curve)
 
     def _supported_curves(self):
         if self._lib.Cryptography_HAS_EC != 1:
diff --git a/cryptography/hazmat/bindings/openssl/conf.py b/cryptography/hazmat/bindings/openssl/conf.py
index dda35e8..001a070 100644
--- a/cryptography/hazmat/bindings/openssl/conf.py
+++ b/cryptography/hazmat/bindings/openssl/conf.py
@@ -22,6 +22,8 @@
 """
 
 FUNCTIONS = """
+void OPENSSL_config(const char *);
+void OPENSSL_no_config(void);
 """
 
 MACROS = """
diff --git a/cryptography/hazmat/primitives/asymmetric/rsa.py b/cryptography/hazmat/primitives/asymmetric/rsa.py
index 481797f..b256ddc 100644
--- a/cryptography/hazmat/primitives/asymmetric/rsa.py
+++ b/cryptography/hazmat/primitives/asymmetric/rsa.py
@@ -21,6 +21,17 @@
 from cryptography.hazmat.primitives import interfaces
 
 
+def generate_private_key(public_exponent, key_size, backend):
+    if not isinstance(backend, RSABackend):
+        raise UnsupportedAlgorithm(
+            "Backend object does not implement RSABackend.",
+            _Reasons.BACKEND_MISSING_INTERFACE
+        )
+
+    _verify_rsa_parameters(public_exponent, key_size)
+    return backend.generate_rsa_private_key(public_exponent, key_size)
+
+
 def _verify_rsa_parameters(public_exponent, key_size):
     if public_exponent < 3:
         raise ValueError("public_exponent must be >= 3.")
diff --git a/docs/hazmat/primitives/asymmetric/ec.rst b/docs/hazmat/primitives/asymmetric/ec.rst
index 798fbab..2c91b5a 100644
--- a/docs/hazmat/primitives/asymmetric/ec.rst
+++ b/docs/hazmat/primitives/asymmetric/ec.rst
@@ -6,7 +6,7 @@
 .. currentmodule:: cryptography.hazmat.primitives.asymmetric.ec
 
 
-,, method:: generate_private_key(curve, backend):
+.. function:: generate_private_key(curve, backend):
 
     .. versionadded:: 0.5
 
@@ -109,7 +109,7 @@
         :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
         provider.
 
-    .. code-block:: pycon
+    .. doctest::
 
         >>> from cryptography.hazmat.backends import default_backend
         >>> from cryptography.hazmat.primitives import hashes
diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst
index 5483911..71b7cd9 100644
--- a/docs/hazmat/primitives/asymmetric/rsa.rst
+++ b/docs/hazmat/primitives/asymmetric/rsa.rst
@@ -7,13 +7,37 @@
 
 `RSA`_ is a `public-key`_ algorithm for encrypting and signing messages.
 
+
+.. function:: generate_private_key(public_exponent, key_size, backend)
+
+    .. versionadded:: 0.5
+
+    Generate an RSA private key using the provided ``backend``.
+
+    :param int public_exponent: The public exponent of the new key.
+        Usually one of the small Fermat primes 3, 5, 17, 257, 65537. If in
+        doubt you should `use 65537`_.
+    :param int key_size: The length of the modulus in bits. For keys
+        generated in 2014 it is strongly recommended to be
+        `at least 2048`_ (See page 41). It must not be less than 512.
+        Some backends may have additional limitations.
+    :param backend: A
+        :class:`~cryptography.hazmat.backends.interfaces.RSABackend`
+        provider.
+    :return: A :class:`~cryptography.hazmat.primitives.interfaces.RSAPrivateKey`
+        provider.
+
+    :raises cryptography.exceptions.UnsupportedAlgorithm: This is raised if
+        the provided ``backend`` does not implement
+        :class:`~cryptography.hazmat.backends.interfaces.RSABackend`
+
 .. class:: RSAPrivateKey(p, q, private_exponent, dmp1, dmq1, iqmp, public_exponent, modulus)
 
     .. versionadded:: 0.2
 
     An RSA private key is required for decryption and signing of messages.
 
-    You should use :meth:`~generate` to generate new keys.
+    You should use :func:`generate_private_key` to generate new keys.
 
     .. warning::
         This method only checks a limited set of properties of its arguments.
diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst
index e53d0d1..874fce8 100644
--- a/docs/hazmat/primitives/asymmetric/serialization.rst
+++ b/docs/hazmat/primitives/asymmetric/serialization.rst
@@ -10,7 +10,7 @@
 key metadata.
 
 Many serialization formats support multiple different types of asymmetric keys
-and will return an an instance of the appropriate type. You should check that
+and will return an instance of the appropriate type. You should check that
 the returned key matches the type your application expects when using these
 methods.
 
diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst
index bca7835..bcb1fb3 100644
--- a/docs/hazmat/primitives/symmetric-encryption.rst
+++ b/docs/hazmat/primitives/symmetric-encryption.rst
@@ -337,7 +337,7 @@
             # Generate a random 96-bit IV.
             iv = os.urandom(12)
 
-            # Construct a AES-GCM Cipher object with the given key and a
+            # Construct an AES-GCM Cipher object with the given key and a
             # randomly generated IV.
             encryptor = Cipher(
                 algorithms.AES(key),
diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py
index 93d5848..64dc062 100644
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -18,12 +18,12 @@
     UnsupportedAlgorithm, _Reasons
 )
 from cryptography.hazmat.backends.interfaces import (
-    CMACBackend, CipherBackend, DSABackend, HMACBackend, HashBackend,
-    PBKDF2HMACBackend, RSABackend
+    CMACBackend, CipherBackend, DSABackend, EllipticCurveBackend, HMACBackend,
+    HashBackend, PBKDF2HMACBackend, RSABackend
 )
 from cryptography.hazmat.backends.multibackend import MultiBackend
 from cryptography.hazmat.primitives import cmac, hashes, hmac
-from cryptography.hazmat.primitives.asymmetric import padding
+from cryptography.hazmat.primitives.asymmetric import ec, padding
 from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
 
 from ...utils import raises_unsupported_algorithm
@@ -154,6 +154,41 @@
             raise UnsupportedAlgorithm("", _Reasons.UNSUPPORTED_CIPHER)
 
 
+@utils.register_interface(EllipticCurveBackend)
+class DummyEllipticCurveBackend(object):
+    def __init__(self, supported_curves):
+        self._curves = supported_curves
+
+    def elliptic_curve_supported(self, curve):
+        return any(
+            isinstance(curve, curve_type)
+            for curve_type in self._curves
+        )
+
+    def elliptic_curve_signature_algorithm_supported(
+        self, signature_algorithm, curve
+    ):
+        return (
+            isinstance(signature_algorithm, ec.ECDSA) and
+            any(
+                isinstance(curve, curve_type)
+                for curve_type in self._curves
+            )
+        )
+
+    def generate_elliptic_curve_private_key(self, curve):
+        if not self.elliptic_curve_supported(curve):
+            raise UnsupportedAlgorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE)
+
+    def elliptic_curve_private_key_from_numbers(self, numbers):
+        if not self.elliptic_curve_supported(numbers.public_numbers.curve):
+            raise UnsupportedAlgorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE)
+
+    def elliptic_curve_public_key_from_numbers(self, numbers):
+        if not self.elliptic_curve_supported(numbers.curve):
+            raise UnsupportedAlgorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE)
+
+
 class TestMultiBackend(object):
     def test_ciphers(self):
         backend = MultiBackend([
@@ -361,3 +396,69 @@
 
         with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER):
             cmac.CMAC(algorithms.TripleDES(fake_key), backend)
+
+    def test_elliptic_curve(self):
+        backend = MultiBackend([
+            DummyEllipticCurveBackend([
+                ec.SECT283K1
+            ])
+        ])
+
+        assert backend.elliptic_curve_supported(ec.SECT283K1()) is True
+
+        assert backend.elliptic_curve_signature_algorithm_supported(
+            ec.ECDSA(hashes.SHA256()),
+            ec.SECT283K1()
+        ) is True
+
+        backend.generate_elliptic_curve_private_key(ec.SECT283K1())
+
+        backend.elliptic_curve_private_key_from_numbers(
+            ec.EllipticCurvePrivateNumbers(
+                1,
+                ec.EllipticCurvePublicNumbers(
+                    2,
+                    3,
+                    ec.SECT283K1()
+                )
+            )
+        )
+
+        backend.elliptic_curve_public_key_from_numbers(
+            ec.EllipticCurvePublicNumbers(
+                2,
+                3,
+                ec.SECT283K1()
+            )
+        )
+
+        assert backend.elliptic_curve_supported(ec.SECT163K1()) is False
+
+        assert backend.elliptic_curve_signature_algorithm_supported(
+            ec.ECDSA(hashes.SHA256()),
+            ec.SECT163K1()
+        ) is False
+
+        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE):
+            backend.generate_elliptic_curve_private_key(ec.SECT163K1())
+
+        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE):
+            backend.elliptic_curve_private_key_from_numbers(
+                ec.EllipticCurvePrivateNumbers(
+                    1,
+                    ec.EllipticCurvePublicNumbers(
+                        2,
+                        3,
+                        ec.SECT163K1()
+                    )
+                )
+            )
+
+        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE):
+            backend.elliptic_curve_public_key_from_numbers(
+                ec.EllipticCurvePublicNumbers(
+                    2,
+                    3,
+                    ec.SECT163K1()
+                )
+            )
diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py
index 610fa62..8f10fb1 100644
--- a/tests/hazmat/primitives/test_rsa.py
+++ b/tests/hazmat/primitives/test_rsa.py
@@ -97,32 +97,38 @@
         )
     )
     def test_generate_rsa_keys(self, backend, public_exponent, key_size):
-        skey = rsa.RSAPrivateKey.generate(public_exponent, key_size, backend)
+        skey = rsa.generate_private_key(public_exponent, key_size, backend)
         _check_rsa_private_key(skey)
         assert skey.key_size == key_size
         assert skey.public_exponent == public_exponent
 
+    def test_generate_rsa_key_class_method(self, backend):
+        skey = rsa.RSAPrivateKey.generate(65537, 512, backend)
+        _check_rsa_private_key(skey)
+        assert skey.key_size == 512
+        assert skey.public_exponent == 65537
+
     def test_generate_bad_public_exponent(self, backend):
         with pytest.raises(ValueError):
-            rsa.RSAPrivateKey.generate(public_exponent=1,
-                                       key_size=2048,
-                                       backend=backend)
+            rsa.generate_private_key(public_exponent=1,
+                                     key_size=2048,
+                                     backend=backend)
 
         with pytest.raises(ValueError):
-            rsa.RSAPrivateKey.generate(public_exponent=4,
-                                       key_size=2048,
-                                       backend=backend)
+            rsa.generate_private_key(public_exponent=4,
+                                     key_size=2048,
+                                     backend=backend)
 
     def test_cant_generate_insecure_tiny_key(self, backend):
         with pytest.raises(ValueError):
-            rsa.RSAPrivateKey.generate(public_exponent=65537,
-                                       key_size=511,
-                                       backend=backend)
+            rsa.generate_private_key(public_exponent=65537,
+                                     key_size=511,
+                                     backend=backend)
 
         with pytest.raises(ValueError):
-            rsa.RSAPrivateKey.generate(public_exponent=65537,
-                                       key_size=256,
-                                       backend=backend)
+            rsa.generate_private_key(public_exponent=65537,
+                                     key_size=256,
+                                     backend=backend)
 
     @pytest.mark.parametrize(
         "pkcs1_example",
@@ -380,6 +386,9 @@
     pretend_backend = object()
 
     with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):
+        rsa.generate_private_key(65537, 2048, pretend_backend)
+
+    with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):
         rsa.RSAPrivateKey.generate(65537, 2048, pretend_backend)
 
 
@@ -966,7 +975,7 @@
 
     def test_rsa_verifier_invalid_backend(self, backend):
         pretend_backend = object()
-        private_key = rsa.RSAPrivateKey.generate(65537, 2048, backend)
+        private_key = rsa.generate_private_key(65537, 2048, backend)
         public_key = private_key.public_key()
 
         with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):