Repository: pyca/cryptography

Issue:
Intermittent AssertionError on PrivateKey.decrypt
I think this is the same issue as seen in #2261

```
  File "/usr/local/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "test_encrypt.py", line 63, in do_encrypt_decrypt
    label=None
  File "/usr/local/lib/python2.7/unittest/case.py", line 475, in assertRaises
    callableObj(*args, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/cryptography/hazmat/backends/openssl/rsa.py", line 534, in decrypt
    return _enc_dec_rsa(self._backend, self, ciphertext, padding)
  File "/usr/local/lib/python2.7/site-packages/cryptography/hazmat/backends/openssl/rsa.py", line 76, in _enc_dec_rsa
    return _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum)
  File "/usr/local/lib/python2.7/site-packages/cryptography/hazmat/backends/openssl/rsa.py", line 105, in _enc_dec_rsa_pkey_ctx
    _handle_rsa_enc_dec_error(backend, key)
  File "/usr/local/lib/python2.7/site-packages/cryptography/hazmat/backends/openssl/rsa.py", line 146, in _handle_rsa_enc_dec_error
    assert errors[0].reason in decoding_errors

AssertionError: 
>>  assert [_OpenSSLError(code=67522692L, lib=4, func=101, reason=132)][0].reason in [106, 107, 121, 159]
    raise ValueError("Decryption failed.")
```

The above assertion error will cause anything from 3-10% of this nose test case to fail when trying to decrypt a payload with the wrong key (decrypt with correct key always works as expected):

```
import json
from nose.tools import assert_equals, assert_not_equals, assert_raises

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import padding, rsa
from cryptography.hazmat.primitives.hashes import SHA1


class TestEncrypt(object):
    def do_encrypt_decrypt(self):
        key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=2048,
            backend=default_backend()
        )

        data = json.dumps(dict(secure='Message'))

        payload = key.public_key().encrypt(
            data,
            padding.OAEP(
                mgf=padding.MGF1(algorithm=SHA1()),
                algorithm=SHA1(),
                label=None
            )
        )
        assert_not_equals(data, payload)

        data_decrypted = key.decrypt(
            payload,
            padding.OAEP(
                mgf=padding.MGF1(algorithm=SHA1()),
                algorithm=SHA1(),
                label=None
            )
        )
        assert_equals(data, data_decrypted)

        another_key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=2048,
            backend=default_backend()
        )
        assert_raises(
            ValueError,
            another_key.decrypt,
            payload,
            padding.OAEP(
                mgf=padding.MGF1(algorithm=SHA1()),
                algorithm=SHA1(),
                label=None
            )
        )

    def test_encrypt_decrypt_repeat(self):
        for _ in xrange(100):
            yield self.do_encrypt_decrypt
```

The reason is always 132 (`RSA_R_DATA_TOO_LARGE_FOR_MODULUS`)

cryptography 1.2.2
OpenSSL 1.0.1k 8 Jan 2015



Discussion:

Resolving patch:
diff --git a/src/_cffi_src/openssl/err.py b/src/_cffi_src/openssl/err.py
--- a/src/_cffi_src/openssl/err.py
+++ b/src/_cffi_src/openssl/err.py
@@ -226,6 +226,7 @@
 static const int PKCS12_R_PKCS12_CIPHERFINAL_ERROR;
 
 static const int RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE;
+static const int RSA_R_DATA_TOO_LARGE_FOR_MODULUS;
 static const int RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY;
 static const int RSA_R_BLOCK_TYPE_IS_NOT_01;
 static const int RSA_R_BLOCK_TYPE_IS_NOT_02;
diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py
--- a/src/cryptography/hazmat/backends/openssl/rsa.py
+++ b/src/cryptography/hazmat/backends/openssl/rsa.py
@@ -139,6 +139,10 @@ def _handle_rsa_enc_dec_error(backend, key):
             backend._lib.RSA_R_BLOCK_TYPE_IS_NOT_01,
             backend._lib.RSA_R_BLOCK_TYPE_IS_NOT_02,
             backend._lib.RSA_R_OAEP_DECODING_ERROR,
+            # Though this error looks similar to the
+            # RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE, this occurs on decrypts,
+            # rather then on encrypts
+            backend._lib.RSA_R_DATA_TOO_LARGE_FOR_MODULUS,
         ]
         if backend._lib.Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR:
             decoding_errors.append(backend._lib.RSA_R_PKCS_DECODING_ERROR)


Test patch:
diff --git a/tests/hazmat/primitives/fixtures_rsa.py b/tests/hazmat/primitives/fixtures_rsa.py
--- a/tests/hazmat/primitives/fixtures_rsa.py
+++ b/tests/hazmat/primitives/fixtures_rsa.py
@@ -529,3 +529,75 @@
             "de04fd053846ca10a223b10cc841cc80fdebee44f3114c13e886af583", 16),
     )
 )
+
+RSA_KEY_2048_ALT = RSAPrivateNumbers(
+    d=int(
+        "7522768467449591813737881904131688860626637897199391200040629"
+        "8641018746450502628484395471408986929218353894683769457466923"
+        "3079369551423094451013669595729568593462009746342148367797495"
+        "5529909313614750246672441810743580455199636293179539903480635"
+        "3091286716112931976896334411287175213124504134181121011488550"
+        "5290054443979198998564749640800633368957384058700741073997703"
+        "8877364695937023906368630297588990131009278072614118207348356"
+        "4640244134189285070202534488517371577359510236833464698189075"
+        "5160693085297816063285814039518178249628112908466649245545732"
+        "5791532385553960363601827996980725025898649392004494256400884"
+        "092073"
+    ),
+    dmp1=int(
+        "5847872614112935747739644055317429405973942336206460017493394"
+        "9737607778799766591021036792892472774720417920838206576785118"
+        "8889624058962939702950175807073343659386156232294197300491647"
+        "1029508414050591959344812347424476498076532682798598325230069"
+        "0925827594762920534235575029199380552228825468180187156871965"
+        "973"
+    ),
+    dmq1=int(
+        "2949536259161239302081155875068405238857801001054083407704879"
+        "8210876832264504685327766351157044892283801611558399025326793"
+        "4131638001934454489864437565651739832511702151461257267169691"
+        "6611992398459006200708626815153304591390855807749769768978152"
+        "9854112656599931724820610358669306523835327459478374630794532"
+        "167"
+    ),
+    iqmp=int(
+        "7331180989818931535458916053540252830484856703208982675535284"
+        "4613815808798190559315018094080936347757336989616401164752221"
+        "8101156529898067044923499386460167055405998646366011838018441"
+        "3678947694258190172377716154009305082091341215866326061721180"
+        "3836418654472188816187630316821692982783286322262994892003058"
+        "782"
+    ),
+    p=int(
+        "1460007723851883695617573533155574746587863843382715314919865"
+        "2434108956187429726002840717317310431378483921058946835896252"
+        "7109559207437158778332364464259678946305487699031865937075508"
+        "8616612925453842458055546540240601585731206561647892336916583"
+        "0023641764106581040198845259766246869529221084602380669333021"
+        "0819"
+    ),
+    q=int(
+        "1433897765867889178402883410610177836503402597775250087462018"
+        "4617952933433119527945447840336616357136736935069377619782227"
+        "2822380830300262175671282877680573202309319960687756231128996"
+        "9764855320953993690199846269451095044922353809602378616938811"
+        "7513900906279873343591486841303392490561500301994171338761080"
+        "4439"
+    ),
+    public_numbers=RSAPublicNumbers(
+        e=65537,
+        n=int(
+            "209350181338107812610165420955871971489973659392253291327"
+            "839812910252466502190690572476688311285621239204212139711"
+            "207388949164851984253143698667018532039612470954223918242"
+            "145976986600705122576087630525229796950722166468064721258"
+            "490916138706756006902066136471049807637157890128560592039"
+            "941717275079733754782848729566190631725183735944031456237"
+            "089928120178187552521649483240599003240074352860189285952"
+            "078970127554801074176375499583703254849309993132931268013"
+            "715070507278514207864914944621214574162116786377990456375"
+            "964817771730371110612100247262908550409785456157505694419"
+            "00451152778245269283276012328748538414051025541"
+        )
+    )
+)
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
@@ -27,8 +27,8 @@
 from .fixtures_rsa import (
     RSA_KEY_1024, RSA_KEY_1025, RSA_KEY_1026, RSA_KEY_1027, RSA_KEY_1028,
     RSA_KEY_1029, RSA_KEY_1030, RSA_KEY_1031, RSA_KEY_1536, RSA_KEY_2048,
-    RSA_KEY_512, RSA_KEY_512_ALT, RSA_KEY_522, RSA_KEY_599, RSA_KEY_745,
-    RSA_KEY_768,
+    RSA_KEY_2048_ALT, RSA_KEY_512, RSA_KEY_512_ALT, RSA_KEY_522, RSA_KEY_599,
+    RSA_KEY_745, RSA_KEY_768,
 )
 from .utils import (
     _check_rsa_private_numbers, generate_rsa_verification_test
@@ -1257,6 +1257,44 @@ def test_invalid_oaep_decryption(self, backend):
                 )
             )
 
+    @pytest.mark.supported(
+        only_if=lambda backend: backend.rsa_padding_supported(
+            padding.OAEP(
+                mgf=padding.MGF1(algorithm=hashes.SHA1()),
+                algorithm=hashes.SHA1(),
+                label=None
+            )
+        ),
+        skip_message="Does not support OAEP."
+    )
+    def test_invalid_oaep_decryption_data_to_large_for_modulus(self, backend):
+        key = RSA_KEY_2048_ALT.private_key(backend)
+
+        ciphertext = (
+            b'\xb1ph\xc0\x0b\x1a|\xe6\xda\xea\xb5\xd7%\x94\x07\xf96\xfb\x96'
+            b'\x11\x9b\xdc4\xea.-\x91\x80\x13S\x94\x04m\xe9\xc5/F\x1b\x9b:\\'
+            b'\x1d\x04\x16ML\xae\xb32J\x01yuA\xbb\x83\x1c\x8f\xf6\xa5\xdbp\xcd'
+            b'\nx\xc7\xf6\x15\xb2/\xdcH\xae\xe7\x13\x13by\r4t\x99\x0fc\x1f\xc1'
+            b'\x1c\xb1\xdd\xc5\x08\xd1\xee\xa1XQ\xb8H@L5v\xc3\xaf\xf2\r\x97'
+            b'\xed\xaa\xe7\xf1\xd4xai\xd3\x83\xd9\xaa9\xbfx\xe1\x87F \x01\xff'
+            b'L\xccv}ae\xb3\xfa\xf2B\xb8\xf9\x04H\x94\x85\xcb\x86\xbb\\ghx!W31'
+            b'\xc7;t\na_E\xc2\x16\xb0;\xa1\x18\t\x1b\xe1\xdb\x80>)\x15\xc6\x12'
+            b'\xcb\xeeg`\x8b\x9b\x1b\x05y4\xb0\x84M6\xcd\xa1\x827o\xfd\x96\xba'
+            b'Z#\x8d\xae\x01\xc9\xf2\xb6\xde\x89{8&eQ\x1e8\x03\x01#?\xb66\\'
+            b'\xad.\xe9\xfa!\x95 c{\xcaz\xe0*\tP\r\x91\x9a)B\xb5\xadN\xf4$\x83'
+            b'\t\xb5u\xab\x19\x99'
+        )
+
+        with pytest.raises(ValueError):
+            key.decrypt(
+                ciphertext,
+                padding.OAEP(
+                    algorithm=hashes.SHA1(),
+                    mgf=padding.MGF1(hashes.SHA1()),
+                    label=None
+                )
+            )
+
     def test_unsupported_oaep_mgf(self, backend):
         private_key = RSA_KEY_512.private_key(backend)
         with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF):