Merge pull request #949 from reaperhulk/rsa-oaep-decrypt

OAEP support for RSA decryption
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 9e89e56..106e0ab 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -11,6 +11,8 @@
   to :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS`. It will be
   removed from ``MGF1`` in two releases per our :doc:`/api-stability` policy.
 
+* Added :class:`~cryptography.hazmat.primitives.cmac.CMAC`.
+
 0.3 - 2014-03-27
 ~~~~~~~~~~~~~~~~
 
diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py
index 86cded8..981a60b 100644
--- a/cryptography/hazmat/backends/multibackend.py
+++ b/cryptography/hazmat/backends/multibackend.py
@@ -16,11 +16,12 @@
 from cryptography import utils
 from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
 from cryptography.hazmat.backends.interfaces import (
-    CipherBackend, DSABackend, HMACBackend, HashBackend, PBKDF2HMACBackend,
-    RSABackend
+    CMACBackend, CipherBackend, DSABackend, HMACBackend, HashBackend,
+    PBKDF2HMACBackend, RSABackend
 )
 
 
+@utils.register_interface(CMACBackend)
 @utils.register_interface(CipherBackend)
 @utils.register_interface(HashBackend)
 @utils.register_interface(HMACBackend)
@@ -156,3 +157,18 @@
             return b.generate_dsa_private_key(parameters)
         raise UnsupportedAlgorithm("DSA is not supported by the backend",
                                    _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+
+    def cmac_algorithm_supported(self, algorithm):
+        return any(
+            b.cmac_algorithm_supported(algorithm)
+            for b in self._filtered_backends(CMACBackend)
+        )
+
+    def create_cmac_ctx(self, algorithm):
+        for b in self._filtered_backends(CMACBackend):
+            try:
+                return b.create_cmac_ctx(algorithm)
+            except UnsupportedAlgorithm:
+                pass
+        raise UnsupportedAlgorithm("This backend does not support CMAC",
+                                   _Reasons.UNSUPPORTED_CIPHER)
diff --git a/cryptography/hazmat/bindings/openssl/evp.py b/cryptography/hazmat/bindings/openssl/evp.py
index 963537c..b3d958e 100644
--- a/cryptography/hazmat/bindings/openssl/evp.py
+++ b/cryptography/hazmat/bindings/openssl/evp.py
@@ -87,6 +87,8 @@
 int EVP_PKEY_bits(EVP_PKEY *);
 int EVP_PKEY_size(EVP_PKEY *);
 RSA *EVP_PKEY_get1_RSA(EVP_PKEY *);
+DSA *EVP_PKEY_get1_DSA(EVP_PKEY *);
+DH *EVP_PKEY_get1_DH(EVP_PKEY *);
 
 int EVP_SignInit(EVP_MD_CTX *, const EVP_MD *);
 int EVP_SignUpdate(EVP_MD_CTX *, const void *, size_t);
@@ -104,6 +106,7 @@
 
 int EVP_PKEY_set1_RSA(EVP_PKEY *, struct rsa_st *);
 int EVP_PKEY_set1_DSA(EVP_PKEY *, struct dsa_st *);
+int EVP_PKEY_set1_DH(EVP_PKEY *, DH *);
 
 int EVP_PKEY_get_attr_count(const EVP_PKEY *);
 int EVP_PKEY_get_attr_by_NID(const EVP_PKEY *, int, int);
@@ -123,7 +126,11 @@
 void OpenSSL_add_all_algorithms(void);
 int EVP_PKEY_assign_RSA(EVP_PKEY *, RSA *);
 int EVP_PKEY_assign_DSA(EVP_PKEY *, DSA *);
+
 int EVP_PKEY_assign_EC_KEY(EVP_PKEY *, EC_KEY *);
+EC_KEY *EVP_PKEY_get1_EC_KEY(EVP_PKEY *);
+int EVP_PKEY_set1_EC_KEY(EVP_PKEY *, EC_KEY *);
+
 int EVP_CIPHER_CTX_block_size(const EVP_CIPHER_CTX *);
 int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *, int, int, void *);
 
@@ -215,6 +222,8 @@
 #endif
 #ifdef OPENSSL_NO_EC
 int (*EVP_PKEY_assign_EC_KEY)(EVP_PKEY *, EC_KEY *) = NULL;
+EC_KEY *(*EVP_PKEY_get1_EC_KEY)(EVP_PKEY *) = NULL;
+int (*EVP_PKEY_set1_EC_KEY)(EVP_PKEY *, EC_KEY *) = NULL;
 #endif
 
 """
@@ -245,5 +254,7 @@
     ],
     "Cryptography_HAS_EC": [
         "EVP_PKEY_assign_EC_KEY",
+        "EVP_PKEY_get1_EC_KEY",
+        "EVP_PKEY_set1_EC_KEY",
     ]
 }
diff --git a/cryptography/hazmat/bindings/openssl/x509.py b/cryptography/hazmat/bindings/openssl/x509.py
index e800d27..9287036 100644
--- a/cryptography/hazmat/bindings/openssl/x509.py
+++ b/cryptography/hazmat/bindings/openssl/x509.py
@@ -160,6 +160,8 @@
 
 int i2d_PrivateKey_bio(BIO *, EVP_PKEY *);
 EVP_PKEY *d2i_PrivateKey_bio(BIO *, EVP_PKEY **);
+int i2d_PUBKEY_bio(BIO *, EVP_PKEY *);
+EVP_PKEY *d2i_PUBKEY_bio(BIO *, EVP_PKEY **);
 
 ASN1_INTEGER *X509_get_serialNumber(X509 *);
 int X509_set_serialNumber(X509 *, ASN1_INTEGER *);
@@ -178,6 +180,22 @@
 const char *X509_get_default_cert_dir_env(void);
 const char *X509_get_default_cert_file_env(void);
 const char *X509_get_default_private_dir(void);
+
+int i2d_RSA_PUBKEY(RSA *, unsigned char **);
+RSA *d2i_RSA_PUBKEY(RSA **, const unsigned char **, long);
+int i2d_DSA_PUBKEY(DSA *, unsigned char **);
+DSA *d2i_DSA_PUBKEY(DSA **, const unsigned char **, long);
+
+RSA *d2i_RSAPrivateKey_bio(BIO *, RSA **);
+int i2d_RSAPrivateKey_bio(BIO *, RSA *);
+RSA *d2i_RSAPublicKey_bio(BIO *, RSA **);
+int i2d_RSAPublicKey_bio(BIO *, RSA *);
+RSA *d2i_RSA_PUBKEY_bio(BIO *, RSA **);
+int i2d_RSA_PUBKEY_bio(BIO *, RSA *);
+DSA *d2i_DSA_PUBKEY_bio(BIO *, DSA **);
+int i2d_DSA_PUBKEY_bio(BIO *, DSA *);
+DSA *d2i_DSAPrivateKey_bio(BIO *, DSA **);
+int i2d_DSAPrivateKey_bio(BIO *, DSA *);
 """
 
 MACROS = """
@@ -213,6 +231,13 @@
    RHEL/CentOS 5 we should move these back to FUNCTIONS. */
 int X509_REQ_add_extensions(X509_REQ *, X509_EXTENSIONS *);
 X509_EXTENSIONS *X509_REQ_get_extensions(X509_REQ *);
+
+int i2d_EC_PUBKEY(EC_KEY *, unsigned char **);
+EC_KEY *d2i_EC_PUBKEY(EC_KEY **, const unsigned char **, long);
+EC_KEY *d2i_EC_PUBKEY_bio(BIO *, EC_KEY **);
+int i2d_EC_PUBKEY_bio(BIO *, EC_KEY *);
+EC_KEY *d2i_ECPrivateKey_bio(BIO *, EC_KEY **);
+int i2d_ECPrivateKey_bio(BIO *, EC_KEY *);
 """
 
 CUSTOMIZATIONS = """
@@ -220,6 +245,23 @@
 #if OPENSSL_VERSION_NUMBER <= 0x0090805fL
 typedef STACK_OF(X509_EXTENSION) X509_EXTENSIONS;
 #endif
+#ifdef OPENSSL_NO_EC
+int (*i2d_EC_PUBKEY)(EC_KEY *, unsigned char **) = NULL;
+EC_KEY *(*d2i_EC_PUBKEY)(EC_KEY **, const unsigned char **, long) = NULL;
+EC_KEY *(*d2i_EC_PUBKEY_bio)(BIO *, EC_KEY **) = NULL;
+int (*i2d_EC_PUBKEY_bio)(BIO *, EC_KEY *) = NULL;
+EC_KEY *(*d2i_ECPrivateKey_bio)(BIO *, EC_KEY **) = NULL;
+int (*i2d_ECPrivateKey_bio)(BIO *, EC_KEY *) = NULL;
+#endif
 """
 
-CONDITIONAL_NAMES = {}
+CONDITIONAL_NAMES = {
+    "Cryptography_HAS_EC": [
+        "i2d_EC_PUBKEY",
+        "d2i_EC_PUBKEY",
+        "d2i_EC_PUBKEY_bio",
+        "i2d_EC_PUBKEY_bio",
+        "d2i_ECPrivateKey_bio",
+        "i2d_ECPrivateKey_bio",
+    ]
+}
diff --git a/docs/hazmat/backends/openssl.rst b/docs/hazmat/backends/openssl.rst
index fdfadf0..43e5d8f 100644
--- a/docs/hazmat/backends/openssl.rst
+++ b/docs/hazmat/backends/openssl.rst
@@ -73,6 +73,6 @@
 
 
 .. _`OpenSSL`: https://www.openssl.org/
-.. _`initializing the RNG`: http://en.wikipedia.org/wiki/OpenSSL#Vulnerability_in_the_Debian_implementation
+.. _`initializing the RNG`: https://en.wikipedia.org/wiki/OpenSSL#Predictable_keys_.28Debian-specific.29
 .. _`Yarrow`: http://en.wikipedia.org/wiki/Yarrow_algorithm
 .. _`Microsoft documentation`: http://msdn.microsoft.com/en-us/library/windows/desktop/aa379942(v=vs.85).aspx
diff --git a/docs/hazmat/primitives/mac/cmac.rst b/docs/hazmat/primitives/mac/cmac.rst
index 8b88a3c..a6b048b 100644
--- a/docs/hazmat/primitives/mac/cmac.rst
+++ b/docs/hazmat/primitives/mac/cmac.rst
@@ -19,10 +19,12 @@
 
 .. class:: CMAC(algorithm, backend)
 
+    .. versionadded:: 0.4
+
     CMAC objects take a
     :class:`~cryptography.hazmat.primitives.interfaces.BlockCipherAlgorithm` provider.
 
-    .. code-block:: pycon
+    .. doctest::
 
         >>> from cryptography.hazmat.backends import default_backend
         >>> from cryptography.hazmat.primitives import cmac
diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py
index f46009d..d8c09bd 100644
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -18,11 +18,11 @@
     UnsupportedAlgorithm, _Reasons
 )
 from cryptography.hazmat.backends.interfaces import (
-    CipherBackend, DSABackend, HMACBackend, HashBackend, PBKDF2HMACBackend,
-    RSABackend
+    CMACBackend, CipherBackend, DSABackend, HMACBackend, HashBackend,
+    PBKDF2HMACBackend, RSABackend
 )
 from cryptography.hazmat.backends.multibackend import MultiBackend
-from cryptography.hazmat.primitives import hashes, hmac
+from cryptography.hazmat.primitives import cmac, hashes, hmac
 from cryptography.hazmat.primitives.asymmetric import padding
 from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
 
@@ -108,6 +108,19 @@
         pass
 
 
+@utils.register_interface(CMACBackend)
+class DummyCMACBackend(object):
+    def __init__(self, supported_algorithms):
+        self._algorithms = supported_algorithms
+
+    def cmac_algorithm_supported(self, algorithm):
+        return type(algorithm) in self._algorithms
+
+    def create_cmac_ctx(self, algorithm):
+        if not self.cmac_algorithm_supported(algorithm):
+            raise UnsupportedAlgorithm("", _Reasons.UNSUPPORTED_CIPHER)
+
+
 class TestMultiBackend(object):
     def test_ciphers(self):
         backend = MultiBackend([
@@ -224,3 +237,18 @@
             _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
         ):
             backend.generate_dsa_private_key(parameters)
+
+    def test_cmac(self):
+        backend = MultiBackend([
+            DummyCMACBackend([algorithms.AES])
+        ])
+
+        fake_key = b"\x00" * 16
+
+        assert backend.cmac_algorithm_supported(
+            algorithms.AES(fake_key)) is True
+
+        cmac.CMAC(algorithms.AES(fake_key), backend)
+
+        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER):
+            cmac.CMAC(algorithms.TripleDES(fake_key), backend)