Repository: pyca/cryptography

Issue:
Re-add CAST5
See #413 



Discussion:
Closing this will be dependent on resolving #393.


Resolving patch:
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
@@ -302,6 +302,9 @@ def generate_rsa_private_key(self, public_exponent, key_size):
         return rsa.RSAPrivateKey(
             p=self._bn_to_int(ctx.p),
             q=self._bn_to_int(ctx.q),
+            dmp1=self._bn_to_int(ctx.dmp1),
+            dmq1=self._bn_to_int(ctx.dmq1),
+            iqmp=self._bn_to_int(ctx.iqmp),
             private_exponent=self._bn_to_int(ctx.d),
             public_exponent=self._bn_to_int(ctx.e),
             modulus=self._bn_to_int(ctx.n),
diff --git a/cryptography/hazmat/primitives/asymmetric/rsa.py b/cryptography/hazmat/primitives/asymmetric/rsa.py
--- a/cryptography/hazmat/primitives/asymmetric/rsa.py
+++ b/cryptography/hazmat/primitives/asymmetric/rsa.py
@@ -72,10 +72,14 @@ def n(self):
 
 @utils.register_interface(interfaces.RSAPrivateKey)
 class RSAPrivateKey(object):
-    def __init__(self, p, q, private_exponent, public_exponent, modulus):
+    def __init__(self, p, q, private_exponent, dmp1, dmq1, iqmp,
+                 public_exponent, modulus):
         if (
             not isinstance(p, six.integer_types) or
             not isinstance(q, six.integer_types) or
+            not isinstance(dmp1, six.integer_types) or
+            not isinstance(dmq1, six.integer_types) or
+            not isinstance(iqmp, six.integer_types) or
             not isinstance(private_exponent, six.integer_types) or
             not isinstance(public_exponent, six.integer_types) or
             not isinstance(modulus, six.integer_types)
@@ -91,6 +95,15 @@ def __init__(self, p, q, private_exponent, public_exponent, modulus):
         if q >= modulus:
             raise ValueError("q must be < modulus")
 
+        if dmp1 >= modulus:
+            raise ValueError("dmp1 must be < modulus")
+
+        if dmq1 >= modulus:
+            raise ValueError("dmq1 must be < modulus")
+
+        if iqmp >= modulus:
+            raise ValueError("iqmp must be < modulus")
+
         if private_exponent >= modulus:
             raise ValueError("private_exponent must be < modulus")
 
@@ -100,11 +113,20 @@ def __init__(self, p, q, private_exponent, public_exponent, modulus):
         if public_exponent & 1 == 0:
             raise ValueError("public_exponent must be odd")
 
+        if dmp1 & 1 == 0:
+            raise ValueError("dmp1 must be odd")
+
+        if dmq1 & 1 == 0:
+            raise ValueError("dmq1 must be odd")
+
         if p * q != modulus:
             raise ValueError("p*q must equal modulus")
 
         self._p = p
         self._q = q
+        self._dmp1 = dmp1
+        self._dmq1 = dmq1
+        self._iqmp = iqmp
         self._private_exponent = private_exponent
         self._public_exponent = public_exponent
         self._modulus = modulus
@@ -144,6 +166,18 @@ def modulus(self):
     def d(self):
         return self.private_exponent
 
+    @property
+    def dmp1(self):
+        return self._dmp1
+
+    @property
+    def dmq1(self):
+        return self._dmq1
+
+    @property
+    def iqmp(self):
+        return self._iqmp
+
     @property
     def e(self):
         return self.public_exponent
diff --git a/cryptography/hazmat/primitives/interfaces.py b/cryptography/hazmat/primitives/interfaces.py
--- a/cryptography/hazmat/primitives/interfaces.py
+++ b/cryptography/hazmat/primitives/interfaces.py
@@ -227,6 +227,27 @@ def d(self):
         private_exponent.
         """
 
+    @abc.abstractproperty
+    def dmp1(self):
+        """
+        A Chinese remainder theorem coefficient used to speed up RSA
+        calculations.  Calculated as: d mod (p-1)
+        """
+
+    @abc.abstractproperty
+    def dmq1(self):
+        """
+        A Chinese remainder theorem coefficient used to speed up RSA
+        calculations.  Calculated as: d mod (q-1)
+        """
+
+    @abc.abstractproperty
+    def iqmp(self):
+        """
+        A Chinese remainder theorem coefficient used to speed up RSA
+        calculations. The modular inverse of q modulo p
+        """
+
     @abc.abstractproperty
     def e(self):
         """


Test patch:
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
@@ -24,6 +24,19 @@
 from ...utils import load_pkcs1_vectors, load_vectors_from_file
 
 
+def _modinv(e, m):
+    """
+    Modular Multiplicative Inverse.  Returns x such that: (x*e) mod m == 1
+    """
+    x1, y1, x2, y2 = 1, 0, 0, 1
+    a, b = e, m
+    while b > 0:
+        q, r = divmod(a, b)
+        xn, yn = x1 - q * x2, y1 - q * y2
+        a, b, x1, y1, x2, y2 = b, r, x2, y2, xn, yn
+    return x1 % m
+
+
 def _check_rsa_private_key(skey):
     assert skey
     assert skey.modulus
@@ -31,6 +44,9 @@ def _check_rsa_private_key(skey):
     assert skey.private_exponent
     assert skey.p * skey.q == skey.modulus
     assert skey.key_size
+    assert skey.dmp1 == skey.d % (skey.p - 1)
+    assert skey.dmq1 == skey.d % (skey.q - 1)
+    assert skey.iqmp == _modinv(skey.q, skey.p)
 
     pkey = skey.public_key()
     assert pkey
@@ -39,6 +55,27 @@ def _check_rsa_private_key(skey):
     assert skey.key_size == pkey.key_size
 
 
+def test_modular_inverse():
+    p = int(
+        "d1f9f6c09fd3d38987f7970247b85a6da84907753d42ec52bc23b745093f4fff5cff3"
+        "617ce43d00121a9accc0051f519c76e08cf02fc18acfe4c9e6aea18da470a2b611d2e"
+        "56a7b35caa2c0239bc041a53cc5875ca0b668ae6377d4b23e932d8c995fd1e58ecfd8"
+        "c4b73259c0d8a54d691cca3f6fb85c8a5c1baf588e898d481", 16
+    )
+    q = int(
+        "d1519255eb8f678c86cfd06802d1fbef8b664441ac46b73d33d13a8404580a33a8e74"
+        "cb2ea2e2963125b3d454d7a922cef24dd13e55f989cbabf64255a736671f4629a47b5"
+        "b2347cfcd669133088d1c159518531025297c2d67c9da856a12e80222cd03b4c6ec0f"
+        "86c957cb7bb8de7a127b645ec9e820aa94581e4762e209f01", 16
+    )
+    assert _modinv(q, p) == int(
+        "0275e06afa722999315f8f322275483e15e2fb46d827b17800f99110b269a6732748f"
+        "624a382fa2ed1ec68c99f7fc56fb60e76eea51614881f497ba7034c17dde955f92f15"
+        "772f8b2b41f3e56d88b1e096cdd293eba4eae1e82db815e0fadea0c4ec971bc6fd875"
+        "c20e67e48c31a611e98d32c6213ae4c4d7b53023b2f80c538", 16
+    )
+
+
 @pytest.mark.rsa
 class TestRSA(object):
     @pytest.mark.parametrize(
@@ -115,22 +152,26 @@ def test_load_pss_vect_example_keys(self, pkcs1_example):
 
     def test_invalid_private_key_argument_types(self):
         with pytest.raises(TypeError):
-            rsa.RSAPrivateKey(None, None, None, None, None)
+            rsa.RSAPrivateKey(None, None, None, None, None, None, None, None)
 
     def test_invalid_public_key_argument_types(self):
         with pytest.raises(TypeError):
             rsa.RSAPublicKey(None, None)
 
     def test_invalid_private_key_argument_values(self):
-        # Start with p=3, q=5, private_exponent=14, public_exponent=7,
-        # modulus=15. Then change one value at a time to test the bounds.
+        # Start with p=3, q=11, private_exponent=3, public_exponent=7,
+        # modulus=33, dmp1=1, dmq1=3, iqmp=2. Then change one value at
+        # a time to test the bounds.
 
         # Test a modulus < 3.
         with pytest.raises(ValueError):
             rsa.RSAPrivateKey(
                 p=3,
-                q=5,
-                private_exponent=14,
+                q=11,
+                private_exponent=3,
+                dmp1=1,
+                dmq1=3,
+                iqmp=2,
                 public_exponent=7,
                 modulus=2
             )
@@ -139,70 +180,156 @@ def test_invalid_private_key_argument_values(self):
         with pytest.raises(ValueError):
             rsa.RSAPrivateKey(
                 p=3,
-                q=5,
-                private_exponent=14,
+                q=11,
+                private_exponent=3,
+                dmp1=1,
+                dmq1=3,
+                iqmp=2,
                 public_exponent=7,
-                modulus=16
+                modulus=35
             )
 
         # Test a p > modulus.
         with pytest.raises(ValueError):
             rsa.RSAPrivateKey(
-                p=16,
-                q=5,
-                private_exponent=14,
+                p=37,
+                q=11,
+                private_exponent=3,
+                dmp1=1,
+                dmq1=3,
+                iqmp=2,
                 public_exponent=7,
-                modulus=15
+                modulus=33
             )
 
         # Test a q > modulus.
         with pytest.raises(ValueError):
             rsa.RSAPrivateKey(
                 p=3,
-                q=16,
-                private_exponent=14,
+                q=37,
+                private_exponent=3,
+                dmp1=1,
+                dmq1=3,
+                iqmp=2,
+                public_exponent=7,
+                modulus=33
+            )
+
+        # Test a dmp1 > modulus.
+        with pytest.raises(ValueError):
+            rsa.RSAPrivateKey(
+                p=3,
+                q=11,
+                private_exponent=3,
+                dmp1=35,
+                dmq1=3,
+                iqmp=2,
+                public_exponent=7,
+                modulus=33
+            )
+
+        # Test a dmq1 > modulus.
+        with pytest.raises(ValueError):
+            rsa.RSAPrivateKey(
+                p=3,
+                q=11,
+                private_exponent=3,
+                dmp1=1,
+                dmq1=35,
+                iqmp=2,
                 public_exponent=7,
-                modulus=15
+                modulus=33
+            )
+
+        # Test an iqmp > modulus.
+        with pytest.raises(ValueError):
+            rsa.RSAPrivateKey(
+                p=3,
+                q=11,
+                private_exponent=3,
+                dmp1=1,
+                dmq1=3,
+                iqmp=35,
+                public_exponent=7,
+                modulus=33
             )
 
         # Test a private_exponent > modulus
         with pytest.raises(ValueError):
             rsa.RSAPrivateKey(
                 p=3,
-                q=5,
-                private_exponent=16,
+                q=11,
+                private_exponent=37,
+                dmp1=1,
+                dmq1=3,
+                iqmp=2,
                 public_exponent=7,
-                modulus=15
+                modulus=33
             )
 
         # Test a public_exponent < 3
         with pytest.raises(ValueError):
             rsa.RSAPrivateKey(
                 p=3,
-                q=5,
-                private_exponent=14,
+                q=11,
+                private_exponent=3,
+                dmp1=1,
+                dmq1=3,
+                iqmp=2,
                 public_exponent=1,
-                modulus=15
+                modulus=33
             )
 
         # Test a public_exponent > modulus
         with pytest.raises(ValueError):
             rsa.RSAPrivateKey(
                 p=3,
-                q=5,
-                private_exponent=14,
-                public_exponent=17,
-                modulus=15
+                q=11,
+                private_exponent=3,
+                dmp1=1,
+                dmq1=3,
+                iqmp=2,
+                public_exponent=65537,
+                modulus=33
             )
 
         # Test a public_exponent that is not odd.
         with pytest.raises(ValueError):
             rsa.RSAPrivateKey(
                 p=3,
-                q=5,
-                private_exponent=14,
+                q=11,
+                private_exponent=3,
+                dmp1=1,
+                dmq1=3,
+                iqmp=2,
                 public_exponent=6,
-                modulus=15
+                modulus=33
+            )
+
+        # Test a dmp1 that is not odd.
+        with pytest.raises(ValueError):
+            rsa.RSAPrivateKey(
+                p=3,
+                q=11,
+                private_exponent=3,
+                dmp1=2,
+                dmq1=3,
+                iqmp=2,
+                public_exponent=7,
+                modulus=33
+            )
+
+        # Test a dmq1 that is not odd.
+        with pytest.raises(ValueError):
+            rsa.RSAPrivateKey(
+                p=3,
+                q=11,
+                private_exponent=3,
+                dmp1=1,
+                dmq1=4,
+                iqmp=2,
+                public_exponent=7,
+                modulus=33
             )
 
     def test_invalid_public_key_argument_values(self):
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -768,7 +768,19 @@ def test_load_pkcs1_vectors():
                 'q': int(
                     '847e732376fc7900f898ea82eb2b0fc418565fdae62f7d9ec4ce2217b'
                     '97990dd272db157f99f63c0dcbb9fbacdbd4c4dadb6df67756358ca41'
-                    '74825b48f49706d', 16)
+                    '74825b48f49706d', 16),
+                'dmp1': int(
+                    '05c2a83c124b3621a2aa57ea2c3efe035eff4560f33ddebb7adab81fc'
+                    'e69a0c8c2edc16520dda83d59a23be867963ac65f2cc710bbcfb96ee1'
+                    '03deb771d105fd85', 16),
+                'dmq1': int(
+                    '04cae8aa0d9faa165c87b682ec140b8ed3b50b24594b7a3b2c220b366'
+                    '9bb819f984f55310a1ae7823651d4a02e99447972595139363434e5e3'
+                    '0a7e7d241551e1b9', 16),
+                'iqmp': int(
+                    '07d3e47bf686600b11ac283ce88dbb3f6051e8efd04680e44c171ef53'
+                    '1b80b2b7c39fc766320e2cf15d8d99820e96ff30dc69691839c4b40d7'
+                    'b06e45307dc91f3f', 16)
             },
 
             {
@@ -809,7 +821,22 @@ def test_load_pkcs1_vectors():
                     'ed4d71d0a6e24b93c2e5f6b4bbe05f5fb0afa042d204fe3378d365c2f'
                     '288b6a8dad7efe45d153eef40cacc7b81ff934002d108994b94a5e472'
                     '8cd9c963375ae49965bda55cbf0efed8d6553b4027f2d86208a6e6b48'
-                    '9c176128092d629e49d3d', 16)
+                    '9c176128092d629e49d3d', 16),
+                'dmp1': int(
+                    '2bb68bddfb0c4f56c8558bffaf892d8043037841e7fa81cfa61a38c5e'
+                    '39b901c8ee71122a5da2227bd6cdeeb481452c12ad3d61d5e4f776a0a'
+                    'b556591befe3e59e5a7fddb8345e1f2f35b9f4cee57c32414c086aec9'
+                    '93e9353e480d9eec6289f', 16),
+                'dmq1': int(
+                    '4ff897709fad079746494578e70fd8546130eeab5627c49b080f05ee4'
+                    'ad9f3e4b7cba9d6a5dff113a41c3409336833f190816d8a6bc42e9bec'
+                    '56b7567d0f3c9c696db619b245d901dd856db7c8092e77e9a1cccd56e'
+                    'e4dba42c5fdb61aec2669', 16),
+                'iqmp': int(
+                    '77b9d1137b50404a982729316efafc7dfe66d34e5a182600d5f30a0a8'
+                    '512051c560d081d4d0a1835ec3d25a60f4e4d6aa948b2bf3dbb5b124c'
+                    'bbc3489255a3a948372f6978496745f943e1db4f18382ceaa505dfc65'
+                    '757bb3f857a58dce52156', 16)
             },
 
             {
diff --git a/tests/utils.py b/tests/utils.py
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -262,6 +262,12 @@ def load_pkcs1_vectors(vector_data):
             attr = "p"
         elif line.startswith("# Prime 2:"):
             attr = "q"
+        elif line.startswith("# Prime exponent 1:"):
+            attr = "dmp1"
+        elif line.startswith("# Prime exponent 2:"):
+            attr = "dmq1"
+        elif line.startswith("# Coefficient:"):
+            attr = "iqmp"
         elif line.startswith("#"):
             attr = None
         else: