diff options
author | alexv-smirnov <alex@ydb.tech> | 2023-12-01 12:02:50 +0300 |
---|---|---|
committer | alexv-smirnov <alex@ydb.tech> | 2023-12-01 13:28:10 +0300 |
commit | 0e578a4c44d4abd539d9838347b9ebafaca41dfb (patch) | |
tree | a0c1969c37f818c830ebeff9c077eacf30be6ef8 /contrib/python/rsa/py3/tests | |
parent | 84f2d3d4cc985e63217cff149bd2e6d67ae6fe22 (diff) | |
download | ydb-0e578a4c44d4abd539d9838347b9ebafaca41dfb.tar.gz |
Change "ya.make"
Diffstat (limited to 'contrib/python/rsa/py3/tests')
-rw-r--r-- | contrib/python/rsa/py3/tests/__init__.py | 0 | ||||
-rw-r--r-- | contrib/python/rsa/py3/tests/private.pem | 5 | ||||
-rw-r--r-- | contrib/python/rsa/py3/tests/test_cli.py | 291 | ||||
-rw-r--r-- | contrib/python/rsa/py3/tests/test_common.py | 83 | ||||
-rw-r--r-- | contrib/python/rsa/py3/tests/test_integers.py | 48 | ||||
-rw-r--r-- | contrib/python/rsa/py3/tests/test_key.py | 87 | ||||
-rw-r--r-- | contrib/python/rsa/py3/tests/test_load_save_keys.py | 234 | ||||
-rw-r--r-- | contrib/python/rsa/py3/tests/test_mypy.py | 31 | ||||
-rw-r--r-- | contrib/python/rsa/py3/tests/test_parallel.py | 20 | ||||
-rw-r--r-- | contrib/python/rsa/py3/tests/test_pem.py | 100 | ||||
-rw-r--r-- | contrib/python/rsa/py3/tests/test_pkcs1.py | 218 | ||||
-rw-r--r-- | contrib/python/rsa/py3/tests/test_pkcs1_v2.py | 79 | ||||
-rw-r--r-- | contrib/python/rsa/py3/tests/test_prime.py | 133 | ||||
-rw-r--r-- | contrib/python/rsa/py3/tests/test_strings.py | 40 | ||||
-rw-r--r-- | contrib/python/rsa/py3/tests/test_transform.py | 53 | ||||
-rw-r--r-- | contrib/python/rsa/py3/tests/ya.make | 28 |
16 files changed, 1450 insertions, 0 deletions
diff --git a/contrib/python/rsa/py3/tests/__init__.py b/contrib/python/rsa/py3/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/contrib/python/rsa/py3/tests/__init__.py diff --git a/contrib/python/rsa/py3/tests/private.pem b/contrib/python/rsa/py3/tests/private.pem new file mode 100644 index 0000000000..1a17279f23 --- /dev/null +++ b/contrib/python/rsa/py3/tests/private.pem @@ -0,0 +1,5 @@ +-----BEGIN RSA PRIVATE KEY----- +MGECAQACEQCvWovlXBvfEeOMZPEleO9NAgMBAAECEA20Y+6fDkaWvC24horBzQEC +CQDdS2PAL/tK4QIJAMratZuNnT3tAghs7iNYA0ZrgQIIQQ5nU93U4fkCCHR55el6 +/K+2 +-----END RSA PRIVATE KEY----- diff --git a/contrib/python/rsa/py3/tests/test_cli.py b/contrib/python/rsa/py3/tests/test_cli.py new file mode 100644 index 0000000000..bb872ea7c6 --- /dev/null +++ b/contrib/python/rsa/py3/tests/test_cli.py @@ -0,0 +1,291 @@ +""" +Unit tests for CLI entry points. +""" + +from __future__ import print_function + +import functools +import io +import os +import sys +import typing +import unittest +from contextlib import contextmanager, redirect_stdout, redirect_stderr + +import rsa +import rsa.cli +import rsa.util + + +@contextmanager +def captured_output() -> typing.Generator: + """Captures output to stdout and stderr""" + + # According to mypy, we're not supposed to change buf_out.buffer. + # However, this is just a test, and it works, hence the 'type: ignore'. + buf_out = io.StringIO() + buf_out.buffer = io.BytesIO() # type: ignore + + buf_err = io.StringIO() + buf_err.buffer = io.BytesIO() # type: ignore + + with redirect_stdout(buf_out), redirect_stderr(buf_err): + yield buf_out, buf_err + + +def get_bytes_out(buf) -> bytes: + return buf.buffer.getvalue() + + +@contextmanager +def cli_args(*new_argv): + """Updates sys.argv[1:] for a single test.""" + + old_args = sys.argv[:] + sys.argv[1:] = [str(arg) for arg in new_argv] + + try: + yield + finally: + sys.argv[1:] = old_args + + +def remove_if_exists(fname): + """Removes a file if it exists.""" + + if os.path.exists(fname): + os.unlink(fname) + + +def cleanup_files(*filenames): + """Makes sure the files don't exist when the test runs, and deletes them afterward.""" + + def remove(): + for fname in filenames: + remove_if_exists(fname) + + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + remove() + try: + return func(*args, **kwargs) + finally: + remove() + + return wrapper + + return decorator + + +class AbstractCliTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + # Ensure there is a key to use + cls.pub_key, cls.priv_key = rsa.newkeys(512) + cls.pub_fname = "%s.pub" % cls.__name__ + cls.priv_fname = "%s.key" % cls.__name__ + + with open(cls.pub_fname, "wb") as outfile: + outfile.write(cls.pub_key.save_pkcs1()) + + with open(cls.priv_fname, "wb") as outfile: + outfile.write(cls.priv_key.save_pkcs1()) + + @classmethod + def tearDownClass(cls): + if hasattr(cls, "pub_fname"): + remove_if_exists(cls.pub_fname) + if hasattr(cls, "priv_fname"): + remove_if_exists(cls.priv_fname) + + def assertExits(self, status_code, func, *args, **kwargs): + try: + func(*args, **kwargs) + except SystemExit as ex: + if status_code == ex.code: + return + self.fail( + "SystemExit() raised by %r, but exited with code %r, expected %r" + % (func, ex.code, status_code) + ) + else: + self.fail("SystemExit() not raised by %r" % func) + + +class KeygenTest(AbstractCliTest): + def test_keygen_no_args(self): + with captured_output(), cli_args(): + self.assertExits(1, rsa.cli.keygen) + + def test_keygen_priv_stdout(self): + with captured_output() as (out, err): + with cli_args(128): + rsa.cli.keygen() + + lines = get_bytes_out(out).splitlines() + self.assertEqual(b"-----BEGIN RSA PRIVATE KEY-----", lines[0]) + self.assertEqual(b"-----END RSA PRIVATE KEY-----", lines[-1]) + + # The key size should be shown on stderr + self.assertTrue("128-bit key" in err.getvalue()) + + @cleanup_files("test_cli_privkey_out.pem") + def test_keygen_priv_out_pem(self): + with captured_output() as (out, err): + with cli_args("--out=test_cli_privkey_out.pem", "--form=PEM", 128): + rsa.cli.keygen() + + # The key size should be shown on stderr + self.assertTrue("128-bit key" in err.getvalue()) + + # The output file should be shown on stderr + self.assertTrue("test_cli_privkey_out.pem" in err.getvalue()) + + # If we can load the file as PEM, it's good enough. + with open("test_cli_privkey_out.pem", "rb") as pemfile: + rsa.PrivateKey.load_pkcs1(pemfile.read()) + + @cleanup_files("test_cli_privkey_out.der") + def test_keygen_priv_out_der(self): + with captured_output() as (out, err): + with cli_args("--out=test_cli_privkey_out.der", "--form=DER", 128): + rsa.cli.keygen() + + # The key size should be shown on stderr + self.assertTrue("128-bit key" in err.getvalue()) + + # The output file should be shown on stderr + self.assertTrue("test_cli_privkey_out.der" in err.getvalue()) + + # If we can load the file as der, it's good enough. + with open("test_cli_privkey_out.der", "rb") as derfile: + rsa.PrivateKey.load_pkcs1(derfile.read(), format="DER") + + @cleanup_files("test_cli_privkey_out.pem", "test_cli_pubkey_out.pem") + def test_keygen_pub_out_pem(self): + with captured_output() as (out, err): + with cli_args( + "--out=test_cli_privkey_out.pem", + "--pubout=test_cli_pubkey_out.pem", + "--form=PEM", + 256, + ): + rsa.cli.keygen() + + # The key size should be shown on stderr + self.assertTrue("256-bit key" in err.getvalue()) + + # The output files should be shown on stderr + self.assertTrue("test_cli_privkey_out.pem" in err.getvalue()) + self.assertTrue("test_cli_pubkey_out.pem" in err.getvalue()) + + # If we can load the file as PEM, it's good enough. + with open("test_cli_pubkey_out.pem", "rb") as pemfile: + rsa.PublicKey.load_pkcs1(pemfile.read()) + + +class EncryptDecryptTest(AbstractCliTest): + def test_empty_decrypt(self): + with captured_output(), cli_args(): + self.assertExits(1, rsa.cli.decrypt) + + def test_empty_encrypt(self): + with captured_output(), cli_args(): + self.assertExits(1, rsa.cli.encrypt) + + @cleanup_files("encrypted.txt", "cleartext.txt") + def test_encrypt_decrypt(self): + with open("cleartext.txt", "wb") as outfile: + outfile.write(b"Hello cleartext RSA users!") + + with cli_args("-i", "cleartext.txt", "--out=encrypted.txt", self.pub_fname): + with captured_output(): + rsa.cli.encrypt() + + with cli_args("-i", "encrypted.txt", self.priv_fname): + with captured_output() as (out, err): + rsa.cli.decrypt() + + # We should have the original cleartext on stdout now. + output = get_bytes_out(out) + self.assertEqual(b"Hello cleartext RSA users!", output) + + @cleanup_files("encrypted.txt", "cleartext.txt") + def test_encrypt_decrypt_unhappy(self): + with open("cleartext.txt", "wb") as outfile: + outfile.write(b"Hello cleartext RSA users!") + + with cli_args("-i", "cleartext.txt", "--out=encrypted.txt", self.pub_fname): + with captured_output(): + rsa.cli.encrypt() + + # Change a few bytes in the encrypted stream. + with open("encrypted.txt", "r+b") as encfile: + encfile.seek(40) + encfile.write(b"hahaha") + + with cli_args("-i", "encrypted.txt", self.priv_fname): + with captured_output() as (out, err): + self.assertRaises(rsa.DecryptionError, rsa.cli.decrypt) + + +class SignVerifyTest(AbstractCliTest): + def test_empty_verify(self): + with captured_output(), cli_args(): + self.assertExits(1, rsa.cli.verify) + + def test_empty_sign(self): + with captured_output(), cli_args(): + self.assertExits(1, rsa.cli.sign) + + @cleanup_files("signature.txt", "cleartext.txt") + def test_sign_verify(self): + with open("cleartext.txt", "wb") as outfile: + outfile.write(b"Hello RSA users!") + + with cli_args("-i", "cleartext.txt", "--out=signature.txt", self.priv_fname, "SHA-256"): + with captured_output(): + rsa.cli.sign() + + with cli_args("-i", "cleartext.txt", self.pub_fname, "signature.txt"): + with captured_output() as (out, err): + rsa.cli.verify() + + self.assertFalse(b"Verification OK" in get_bytes_out(out)) + + @cleanup_files("signature.txt", "cleartext.txt") + def test_sign_verify_unhappy(self): + with open("cleartext.txt", "wb") as outfile: + outfile.write(b"Hello RSA users!") + + with cli_args("-i", "cleartext.txt", "--out=signature.txt", self.priv_fname, "SHA-256"): + with captured_output(): + rsa.cli.sign() + + # Change a few bytes in the cleartext file. + with open("cleartext.txt", "r+b") as encfile: + encfile.seek(6) + encfile.write(b"DSA") + + with cli_args("-i", "cleartext.txt", self.pub_fname, "signature.txt"): + with captured_output() as (out, err): + self.assertExits("Verification failed.", rsa.cli.verify) + + +class PrivatePublicTest(AbstractCliTest): + """Test CLI command to convert a private to a public key.""" + + @cleanup_files("test_private_to_public.pem") + def test_private_to_public(self): + + with cli_args("-i", self.priv_fname, "-o", "test_private_to_public.pem"): + with captured_output(): + rsa.util.private_to_public() + + # Check that the key is indeed valid. + with open("test_private_to_public.pem", "rb") as pemfile: + key = rsa.PublicKey.load_pkcs1(pemfile.read()) + + self.assertEqual(self.priv_key.n, key.n) + self.assertEqual(self.priv_key.e, key.e) diff --git a/contrib/python/rsa/py3/tests/test_common.py b/contrib/python/rsa/py3/tests/test_common.py new file mode 100644 index 0000000000..c6a60d5acd --- /dev/null +++ b/contrib/python/rsa/py3/tests/test_common.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import struct +from rsa.common import byte_size, bit_size, inverse + + +class TestByteSize(unittest.TestCase): + def test_values(self): + self.assertEqual(byte_size(1 << 1023), 128) + self.assertEqual(byte_size((1 << 1024) - 1), 128) + self.assertEqual(byte_size(1 << 1024), 129) + self.assertEqual(byte_size(255), 1) + self.assertEqual(byte_size(256), 2) + self.assertEqual(byte_size(0xFFFF), 2) + self.assertEqual(byte_size(0xFFFFFF), 3) + self.assertEqual(byte_size(0xFFFFFFFF), 4) + self.assertEqual(byte_size(0xFFFFFFFFFF), 5) + self.assertEqual(byte_size(0xFFFFFFFFFFFF), 6) + self.assertEqual(byte_size(0xFFFFFFFFFFFFFF), 7) + self.assertEqual(byte_size(0xFFFFFFFFFFFFFFFF), 8) + + def test_zero(self): + self.assertEqual(byte_size(0), 1) + + def test_bad_type(self): + self.assertRaises(TypeError, byte_size, []) + self.assertRaises(TypeError, byte_size, ()) + self.assertRaises(TypeError, byte_size, dict()) + self.assertRaises(TypeError, byte_size, "") + self.assertRaises(TypeError, byte_size, None) + + +class TestBitSize(unittest.TestCase): + def test_zero(self): + self.assertEqual(bit_size(0), 0) + + def test_values(self): + self.assertEqual(bit_size(1023), 10) + self.assertEqual(bit_size(1024), 11) + self.assertEqual(bit_size(1025), 11) + self.assertEqual(bit_size(1 << 1024), 1025) + self.assertEqual(bit_size((1 << 1024) + 1), 1025) + self.assertEqual(bit_size((1 << 1024) - 1), 1024) + + def test_negative_values(self): + self.assertEqual(bit_size(-1023), 10) + self.assertEqual(bit_size(-1024), 11) + self.assertEqual(bit_size(-1025), 11) + self.assertEqual(bit_size(-1 << 1024), 1025) + self.assertEqual(bit_size(-((1 << 1024) + 1)), 1025) + self.assertEqual(bit_size(-((1 << 1024) - 1)), 1024) + + def test_bad_type(self): + self.assertRaises(TypeError, bit_size, []) + self.assertRaises(TypeError, bit_size, ()) + self.assertRaises(TypeError, bit_size, dict()) + self.assertRaises(TypeError, bit_size, "") + self.assertRaises(TypeError, bit_size, None) + self.assertRaises(TypeError, bit_size, 0.0) + + +class TestInverse(unittest.TestCase): + def test_normal(self): + self.assertEqual(3, inverse(7, 4)) + self.assertEqual(9, inverse(5, 11)) + + def test_not_relprime(self): + self.assertRaises(ValueError, inverse, 4, 8) + self.assertRaises(ValueError, inverse, 25, 5) diff --git a/contrib/python/rsa/py3/tests/test_integers.py b/contrib/python/rsa/py3/tests/test_integers.py new file mode 100644 index 0000000000..659e85ae95 --- /dev/null +++ b/contrib/python/rsa/py3/tests/test_integers.py @@ -0,0 +1,48 @@ +# Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests integer operations.""" + +import unittest + +import rsa +import rsa.core + + +class IntegerTest(unittest.TestCase): + def setUp(self): + (self.pub, self.priv) = rsa.newkeys(64) + + def test_enc_dec(self): + message = 42 + print("\n\tMessage: %d" % message) + + encrypted = rsa.core.encrypt_int(message, self.pub.e, self.pub.n) + print("\tEncrypted: %d" % encrypted) + + decrypted = rsa.core.decrypt_int(encrypted, self.priv.d, self.pub.n) + print("\tDecrypted: %d" % decrypted) + + self.assertEqual(message, decrypted) + + def test_sign_verify(self): + message = 42 + + signed = rsa.core.encrypt_int(message, self.priv.d, self.pub.n) + print("\n\tSigned: %d" % signed) + + verified = rsa.core.decrypt_int(signed, self.pub.e, self.pub.n) + print("\tVerified: %d" % verified) + + self.assertEqual(message, verified) diff --git a/contrib/python/rsa/py3/tests/test_key.py b/contrib/python/rsa/py3/tests/test_key.py new file mode 100644 index 0000000000..c570830ccc --- /dev/null +++ b/contrib/python/rsa/py3/tests/test_key.py @@ -0,0 +1,87 @@ +""" +Some tests for the rsa/key.py file. +""" + +import unittest + +import rsa.key +import rsa.core + + +class BlindingTest(unittest.TestCase): + def test_blinding(self): + """Test blinding and unblinding. + + This is basically the doctest of the PrivateKey.blind method, but then + implemented as unittest to allow running on different Python versions. + """ + + pk = rsa.key.PrivateKey(3727264081, 65537, 3349121513, 65063, 57287) + + message = 12345 + encrypted = rsa.core.encrypt_int(message, pk.e, pk.n) + + blinded_1, unblind_1 = pk.blind(encrypted) # blind before decrypting + decrypted = rsa.core.decrypt_int(blinded_1, pk.d, pk.n) + unblinded_1 = pk.unblind(decrypted, unblind_1) + + self.assertEqual(unblinded_1, message) + + # Re-blinding should use a different blinding factor. + blinded_2, unblind_2 = pk.blind(encrypted) # blind before decrypting + self.assertNotEqual(blinded_1, blinded_2) + + # The unblinding should still work, though. + decrypted = rsa.core.decrypt_int(blinded_2, pk.d, pk.n) + unblinded_2 = pk.unblind(decrypted, unblind_2) + self.assertEqual(unblinded_2, message) + + +class KeyGenTest(unittest.TestCase): + def test_custom_exponent(self): + pub, priv = rsa.key.newkeys(16, exponent=3) + + self.assertEqual(3, priv.e) + self.assertEqual(3, pub.e) + + def test_default_exponent(self): + pub, priv = rsa.key.newkeys(16) + + self.assertEqual(0x10001, priv.e) + self.assertEqual(0x10001, pub.e) + + def test_exponents_coefficient_calculation(self): + pk = rsa.key.PrivateKey(3727264081, 65537, 3349121513, 65063, 57287) + + self.assertEqual(pk.exp1, 55063) + self.assertEqual(pk.exp2, 10095) + self.assertEqual(pk.coef, 50797) + + def test_custom_getprime_func(self): + # List of primes to test with, in order [p, q, p, q, ....] + # By starting with two of the same primes, we test that this is + # properly rejected. + primes = [64123, 64123, 64123, 50957, 39317, 33107] + + def getprime(_): + return primes.pop(0) + + # This exponent will cause two other primes to be generated. + exponent = 136407 + + (p, q, e, d) = rsa.key.gen_keys( + 64, accurate=False, getprime_func=getprime, exponent=exponent + ) + self.assertEqual(39317, p) + self.assertEqual(33107, q) + + +class HashTest(unittest.TestCase): + """Test hashing of keys""" + + def test_hash_possible(self): + pub, priv = rsa.key.newkeys(16) + + # This raises a TypeError when hashing isn't possible. + hash(priv) + hash(pub) diff --git a/contrib/python/rsa/py3/tests/test_load_save_keys.py b/contrib/python/rsa/py3/tests/test_load_save_keys.py new file mode 100644 index 0000000000..9b8e0d0a05 --- /dev/null +++ b/contrib/python/rsa/py3/tests/test_load_save_keys.py @@ -0,0 +1,234 @@ +# Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unittest for saving and loading keys.""" + +import base64 +import os.path +import pickle +import unittest +import warnings +from unittest import mock + +import rsa.key + +B64PRIV_DER = b"MC4CAQACBQDeKYlRAgMBAAECBQDHn4npAgMA/icCAwDfxwIDANcXAgInbwIDAMZt" +PRIVATE_DER = base64.standard_b64decode(B64PRIV_DER) + +B64PUB_DER = b"MAwCBQDeKYlRAgMBAAE=" +PUBLIC_DER = base64.standard_b64decode(B64PUB_DER) + +PRIVATE_PEM = ( + b"""\ +-----BEGIN CONFUSING STUFF----- +Cruft before the key + +-----BEGIN RSA PRIVATE KEY----- +Comment: something blah + +""" + + B64PRIV_DER + + b""" +-----END RSA PRIVATE KEY----- + +Stuff after the key +-----END CONFUSING STUFF----- +""" +) + +CLEAN_PRIVATE_PEM = ( + b"""\ +-----BEGIN RSA PRIVATE KEY----- +""" + + B64PRIV_DER + + b""" +-----END RSA PRIVATE KEY----- +""" +) + +PUBLIC_PEM = ( + b"""\ +-----BEGIN CONFUSING STUFF----- +Cruft before the key + +-----BEGIN RSA PUBLIC KEY----- +Comment: something blah + +""" + + B64PUB_DER + + b""" +-----END RSA PUBLIC KEY----- + +Stuff after the key +-----END CONFUSING STUFF----- +""" +) + +CLEAN_PUBLIC_PEM = ( + b"""\ +-----BEGIN RSA PUBLIC KEY----- +""" + + B64PUB_DER + + b""" +-----END RSA PUBLIC KEY----- +""" +) + + +class DerTest(unittest.TestCase): + """Test saving and loading DER keys.""" + + def test_load_private_key(self): + """Test loading private DER keys.""" + + key = rsa.key.PrivateKey.load_pkcs1(PRIVATE_DER, "DER") + expected = rsa.key.PrivateKey(3727264081, 65537, 3349121513, 65063, 57287) + + self.assertEqual(expected, key) + self.assertEqual(key.exp1, 55063) + self.assertEqual(key.exp2, 10095) + self.assertEqual(key.coef, 50797) + + @mock.patch("pyasn1.codec.der.decoder.decode") + def test_load_malformed_private_key(self, der_decode): + """Test loading malformed private DER keys.""" + + # Decode returns an invalid exp2 value. + der_decode.return_value = ( + [0, 3727264081, 65537, 3349121513, 65063, 57287, 55063, 0, 50797], + 0, + ) + + with warnings.catch_warnings(record=True) as w: + # Always print warnings + warnings.simplefilter("always") + + # Load 3 keys + for _ in range(3): + key = rsa.key.PrivateKey.load_pkcs1(PRIVATE_DER, "DER") + + # Check that 3 warnings were generated. + self.assertEqual(3, len(w)) + + for warning in w: + self.assertTrue(issubclass(warning.category, UserWarning)) + self.assertIn("malformed", str(warning.message)) + + # Check that we are creating the key with correct values + self.assertEqual(key.exp1, 55063) + self.assertEqual(key.exp2, 10095) + self.assertEqual(key.coef, 50797) + + def test_save_private_key(self): + """Test saving private DER keys.""" + + key = rsa.key.PrivateKey(3727264081, 65537, 3349121513, 65063, 57287) + der = key.save_pkcs1("DER") + + self.assertIsInstance(der, bytes) + self.assertEqual(PRIVATE_DER, der) + + def test_load_public_key(self): + """Test loading public DER keys.""" + + key = rsa.key.PublicKey.load_pkcs1(PUBLIC_DER, "DER") + expected = rsa.key.PublicKey(3727264081, 65537) + + self.assertEqual(expected, key) + + def test_save_public_key(self): + """Test saving public DER keys.""" + + key = rsa.key.PublicKey(3727264081, 65537) + der = key.save_pkcs1("DER") + + self.assertIsInstance(der, bytes) + self.assertEqual(PUBLIC_DER, der) + + +class PemTest(unittest.TestCase): + """Test saving and loading PEM keys.""" + + def test_load_private_key(self): + """Test loading private PEM files.""" + + key = rsa.key.PrivateKey.load_pkcs1(PRIVATE_PEM, "PEM") + expected = rsa.key.PrivateKey(3727264081, 65537, 3349121513, 65063, 57287) + + self.assertEqual(expected, key) + self.assertEqual(key.exp1, 55063) + self.assertEqual(key.exp2, 10095) + self.assertEqual(key.coef, 50797) + + def test_save_private_key(self): + """Test saving private PEM files.""" + + key = rsa.key.PrivateKey(3727264081, 65537, 3349121513, 65063, 57287) + pem = key.save_pkcs1("PEM") + + self.assertIsInstance(pem, bytes) + self.assertEqual(CLEAN_PRIVATE_PEM, pem) + + def test_load_public_key(self): + """Test loading public PEM files.""" + + key = rsa.key.PublicKey.load_pkcs1(PUBLIC_PEM, "PEM") + expected = rsa.key.PublicKey(3727264081, 65537) + + self.assertEqual(expected, key) + + def test_save_public_key(self): + """Test saving public PEM files.""" + + key = rsa.key.PublicKey(3727264081, 65537) + pem = key.save_pkcs1("PEM") + + self.assertIsInstance(pem, bytes) + self.assertEqual(CLEAN_PUBLIC_PEM, pem) + + def test_load_from_disk(self): + """Test loading a PEM file from disk.""" + from yatest.common import source_path + + fname = source_path("contrib/python/rsa/py3/tests/private.pem") + with open(fname, mode="rb") as privatefile: + keydata = privatefile.read() + privkey = rsa.key.PrivateKey.load_pkcs1(keydata) + + self.assertEqual(15945948582725241569, privkey.p) + self.assertEqual(14617195220284816877, privkey.q) + + +class PickleTest(unittest.TestCase): + """Test saving and loading keys by pickling.""" + + def test_private_key(self): + pk = rsa.key.PrivateKey(3727264081, 65537, 3349121513, 65063, 57287) + + pickled = pickle.dumps(pk) + unpickled = pickle.loads(pickled) + self.assertEqual(pk, unpickled) + + for attr in rsa.key.AbstractKey.__slots__: + self.assertTrue(hasattr(unpickled, attr)) + + def test_public_key(self): + pk = rsa.key.PublicKey(3727264081, 65537) + + pickled = pickle.dumps(pk) + unpickled = pickle.loads(pickled) + + self.assertEqual(pk, unpickled) + for attr in rsa.key.AbstractKey.__slots__: + self.assertTrue(hasattr(unpickled, attr)) diff --git a/contrib/python/rsa/py3/tests/test_mypy.py b/contrib/python/rsa/py3/tests/test_mypy.py new file mode 100644 index 0000000000..8cc0d59650 --- /dev/null +++ b/contrib/python/rsa/py3/tests/test_mypy.py @@ -0,0 +1,31 @@ +import pathlib +import sys +import unittest + +import mypy.api + +test_modules = ["rsa", "tests"] + + +class MypyRunnerTest(unittest.TestCase): + def test_run_mypy(self): + proj_root = pathlib.Path(__file__).parent.parent + args = [ + "--incremental", + "--ignore-missing-imports", + f"--python-version={sys.version_info.major}.{sys.version_info.minor}", + ] + [str(proj_root / dirname) for dirname in test_modules] + + result = mypy.api.run(args) + + stdout, stderr, status = result + + messages = [] + if stderr: + messages.append(stderr) + if stdout: + messages.append(stdout) + if status: + messages.append("Mypy failed with status %d" % status) + if messages and not all("Success" in message for message in messages): + self.fail("\n".join(["Mypy errors:"] + messages)) diff --git a/contrib/python/rsa/py3/tests/test_parallel.py b/contrib/python/rsa/py3/tests/test_parallel.py new file mode 100644 index 0000000000..1a69e9ece6 --- /dev/null +++ b/contrib/python/rsa/py3/tests/test_parallel.py @@ -0,0 +1,20 @@ +"""Test for multiprocess prime generation.""" + +import unittest + +import rsa.prime +import rsa.parallel +import rsa.common + + +class ParallelTest(unittest.TestCase): + """Tests for multiprocess prime generation.""" + + def test_parallel_primegen(self): + p = rsa.parallel.getprime(1024, 3) + + self.assertFalse(rsa.prime.is_prime(p - 1)) + self.assertTrue(rsa.prime.is_prime(p)) + self.assertFalse(rsa.prime.is_prime(p + 1)) + + self.assertEqual(1024, rsa.common.bit_size(p)) diff --git a/contrib/python/rsa/py3/tests/test_pem.py b/contrib/python/rsa/py3/tests/test_pem.py new file mode 100644 index 0000000000..7440431fc0 --- /dev/null +++ b/contrib/python/rsa/py3/tests/test_pem.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +# Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from rsa.pem import _markers +import rsa.key + +# 512-bit key. Too small for practical purposes, but good enough for testing with. +public_key_pem = """ +-----BEGIN PUBLIC KEY----- +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKH0aYP9ZFuctlPnXhEyHjgc8ltKKx9M +0c+h4sKMXwjhjbQAZdtWIw8RRghpUJnKj+6bN2XzZDazyULxgPhtax0CAwEAAQ== +-----END PUBLIC KEY----- +""" + +private_key_pem = """ +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBAKH0aYP9ZFuctlPnXhEyHjgc8ltKKx9M0c+h4sKMXwjhjbQAZdtW +Iw8RRghpUJnKj+6bN2XzZDazyULxgPhtax0CAwEAAQJADwR36EpNzQTqDzusCFIq +ZS+h9X8aIovgBK3RNhMIGO2ThpsnhiDTcqIvgQ56knbl6B2W4iOl54tJ6CNtf6l6 +zQIhANTaNLFGsJfOvZHcI0WL1r89+1A4JVxR+lpslJJwAvgDAiEAwsjqqZ2wY2F0 +F8p1J98BEbtjU2mEZIVCMn6vQuhWdl8CIDRL4IJl4eGKlB0QP0JJF1wpeGO/R76l +DaPF5cMM7k3NAiEAss28m/ck9BWBfFVdNjx/vsdFZkx2O9AX9EJWoBSnSgECIQCa ++sVQMUVJFGsdE/31C7wCIbE3IpB7ziABZ7mN+V3Dhg== +-----END RSA PRIVATE KEY----- +""" + +# Private key components +prime1 = 96275860229939261876671084930484419185939191875438854026071315955024109172739 +prime2 = 88103681619592083641803383393198542599284510949756076218404908654323473741407 + + +class TestMarkers(unittest.TestCase): + def test_values(self): + self.assertEqual( + _markers("RSA PRIVATE KEY"), + (b"-----BEGIN RSA PRIVATE KEY-----", b"-----END RSA PRIVATE KEY-----"), + ) + + +class TestBytesAndStrings(unittest.TestCase): + """Test that we can use PEM in both Unicode strings and bytes.""" + + def test_unicode_public(self): + key = rsa.key.PublicKey.load_pkcs1_openssl_pem(public_key_pem) + self.assertEqual(prime1 * prime2, key.n) + + def test_bytes_public(self): + key = rsa.key.PublicKey.load_pkcs1_openssl_pem(public_key_pem.encode("ascii")) + self.assertEqual(prime1 * prime2, key.n) + + def test_unicode_private(self): + key = rsa.key.PrivateKey.load_pkcs1(private_key_pem) + self.assertEqual(prime1 * prime2, key.n) + + def test_bytes_private(self): + key = rsa.key.PrivateKey.load_pkcs1(private_key_pem.encode("ascii")) + self.assertEqual(prime1, key.p) + self.assertEqual(prime2, key.q) + + +class TestByteOutput(unittest.TestCase): + """Tests that PEM and DER are returned as bytes.""" + + def test_bytes_public(self): + key = rsa.key.PublicKey.load_pkcs1_openssl_pem(public_key_pem) + self.assertIsInstance(key.save_pkcs1(format="DER"), bytes) + self.assertIsInstance(key.save_pkcs1(format="PEM"), bytes) + + def test_bytes_private(self): + key = rsa.key.PrivateKey.load_pkcs1(private_key_pem) + self.assertIsInstance(key.save_pkcs1(format="DER"), bytes) + self.assertIsInstance(key.save_pkcs1(format="PEM"), bytes) + + +class TestByteInput(unittest.TestCase): + """Tests that PEM and DER can be loaded from bytes.""" + + def test_bytes_public(self): + key = rsa.key.PublicKey.load_pkcs1_openssl_pem(public_key_pem.encode("ascii")) + self.assertIsInstance(key.save_pkcs1(format="DER"), bytes) + self.assertIsInstance(key.save_pkcs1(format="PEM"), bytes) + + def test_bytes_private(self): + key = rsa.key.PrivateKey.load_pkcs1(private_key_pem.encode("ascii")) + self.assertIsInstance(key.save_pkcs1(format="DER"), bytes) + self.assertIsInstance(key.save_pkcs1(format="PEM"), bytes) diff --git a/contrib/python/rsa/py3/tests/test_pkcs1.py b/contrib/python/rsa/py3/tests/test_pkcs1.py new file mode 100644 index 0000000000..a8b3cfdee9 --- /dev/null +++ b/contrib/python/rsa/py3/tests/test_pkcs1.py @@ -0,0 +1,218 @@ +# Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests string operations.""" + +import struct +import sys +import unittest + +import rsa +from rsa import pkcs1 + + +class BinaryTest(unittest.TestCase): + def setUp(self): + (self.pub, self.priv) = rsa.newkeys(256) + + def test_enc_dec(self): + message = struct.pack(">IIII", 0, 0, 0, 1) + print("\n\tMessage: %r" % message) + + encrypted = pkcs1.encrypt(message, self.pub) + print("\tEncrypted: %r" % encrypted) + + decrypted = pkcs1.decrypt(encrypted, self.priv) + print("\tDecrypted: %r" % decrypted) + + self.assertEqual(message, decrypted) + + def test_decoding_failure(self): + message = struct.pack(">IIII", 0, 0, 0, 1) + encrypted = pkcs1.encrypt(message, self.pub) + + # Alter the encrypted stream + a = encrypted[5] + self.assertIsInstance(a, int) + + altered_a = (a + 1) % 256 + encrypted = encrypted[:5] + bytes([altered_a]) + encrypted[6:] + + self.assertRaises(pkcs1.DecryptionError, pkcs1.decrypt, encrypted, self.priv) + + def test_randomness(self): + """Encrypting the same message twice should result in different + cryptos. + """ + + message = struct.pack(">IIII", 0, 0, 0, 1) + encrypted1 = pkcs1.encrypt(message, self.pub) + encrypted2 = pkcs1.encrypt(message, self.pub) + + self.assertNotEqual(encrypted1, encrypted2) + + +class ExtraZeroesTest(unittest.TestCase): + def setUp(self): + # Key, cyphertext, and plaintext taken from https://github.com/sybrenstuvel/python-rsa/issues/146 + self.private_key = rsa.PrivateKey.load_pkcs1( + "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAs1EKK81M5kTFtZSuUFnhKy8FS2WNXaWVmi/fGHG4CLw98+Yo\n0nkuUarVwSS0O9pFPcpc3kvPKOe9Tv+6DLS3Qru21aATy2PRqjqJ4CYn71OYtSwM\n/ZfSCKvrjXybzgu+sBmobdtYm+sppbdL+GEHXGd8gdQw8DDCZSR6+dPJFAzLZTCd\nB+Ctwe/RXPF+ewVdfaOGjkZIzDoYDw7n+OHnsYCYozkbTOcWHpjVevipR+IBpGPi\n1rvKgFnlcG6d/tj0hWRl/6cS7RqhjoiNEtxqoJzpXs/Kg8xbCxXbCchkf11STA8u\ndiCjQWuWI8rcDwl69XMmHJjIQAqhKvOOQ8rYTQIDAQABAoIBABpQLQ7qbHtp4h1Y\nORAfcFRW7Q74UvtH/iEHH1TF8zyM6wZsYtcn4y0mxYE3Mp+J0xlTJbeVJkwZXYVH\nL3UH29CWHSlR+TWiazTwrCTRVJDhEoqbcTiRW8fb+o/jljVxMcVDrpyYUHNo2c6w\njBxhmKPtp66hhaDpds1Cwi0A8APZ8Z2W6kya/L/hRBzMgCz7Bon1nYBMak5PQEwV\nF0dF7Wy4vIjvCzO6DSqA415DvJDzUAUucgFudbANNXo4HJwNRnBpymYIh8mHdmNJ\n/MQ0YLSqUWvOB57dh7oWQwe3UsJ37ZUorTugvxh3NJ7Tt5ZqbCQBEECb9ND63gxo\n/a3YR/0CgYEA7BJc834xCi/0YmO5suBinWOQAF7IiRPU+3G9TdhWEkSYquupg9e6\nK9lC5k0iP+t6I69NYF7+6mvXDTmv6Z01o6oV50oXaHeAk74O3UqNCbLe9tybZ/+F\ndkYlwuGSNttMQBzjCiVy0+y0+Wm3rRnFIsAtd0RlZ24aN3bFTWJINIsCgYEAwnQq\nvNmJe9SwtnH5c/yCqPhKv1cF/4jdQZSGI6/p3KYNxlQzkHZ/6uvrU5V27ov6YbX8\nvKlKfO91oJFQxUD6lpTdgAStI3GMiJBJIZNpyZ9EWNSvwUj28H34cySpbZz3s4Xd\nhiJBShgy+fKURvBQwtWmQHZJ3EGrcOI7PcwiyYcCgYEAlql5jSUCY0ALtidzQogW\nJ+B87N+RGHsBuJ/0cxQYinwg+ySAAVbSyF1WZujfbO/5+YBN362A/1dn3lbswCnH\nK/bHF9+fZNqvwprPnceQj5oK1n4g6JSZNsy6GNAhosT+uwQ0misgR8SQE4W25dDG\nkdEYsz+BgCsyrCcu8J5C+tUCgYAFVPQbC4f2ikVyKzvgz0qx4WUDTBqRACq48p6e\n+eLatv7nskVbr7QgN+nS9+Uz80ihR0Ev1yCAvnwmM/XYAskcOea87OPmdeWZlQM8\nVXNwINrZ6LMNBLgorfuTBK1UoRo1pPUHCYdqxbEYI2unak18mikd2WB7Fp3h0YI4\nVpGZnwKBgBxkAYnZv+jGI4MyEKdsQgxvROXXYOJZkWzsKuKxVkVpYP2V4nR2YMOJ\nViJQ8FUEnPq35cMDlUk4SnoqrrHIJNOvcJSCqM+bWHAioAsfByLbUPM8sm3CDdIk\nXVJl32HuKYPJOMIWfc7hIfxLRHnCN+coz2M6tgqMDs0E/OfjuqVZ\n-----END RSA PRIVATE KEY-----", + format="PEM", + ) + self.cyphertext = bytes.fromhex( + "4501b4d669e01b9ef2dc800aa1b06d49196f5a09fe8fbcd037323c60eaf027bfb98432be4e4a26c567ffec718bcbea977dd26812fa071c33808b4d5ebb742d9879806094b6fbeea63d25ea3141733b60e31c6912106e1b758a7fe0014f075193faa8b4622bfd5d3013f0a32190a95de61a3604711bc62945f95a6522bd4dfed0a994ef185b28c281f7b5e4c8ed41176d12d9fc1b837e6a0111d0132d08a6d6f0580de0c9eed8ed105531799482d1e466c68c23b0c222af7fc12ac279bc4ff57e7b4586d209371b38c4c1035edd418dc5f960441cb21ea2bedbfea86de0d7861e81021b650a1de51002c315f1e7c12debe4dcebf790caaa54a2f26b149cf9e77d" + ) + self.plaintext = bytes.fromhex("54657374") + + def test_unmodified(self): + message = rsa.decrypt(self.cyphertext, self.private_key) + self.assertEqual(message, self.plaintext) + + def test_prepend_zeroes(self): + cyphertext = bytes.fromhex("0000") + self.cyphertext + with self.assertRaises(rsa.DecryptionError): + rsa.decrypt(cyphertext, self.private_key) + + def test_append_zeroes(self): + cyphertext = self.cyphertext + bytes.fromhex("0000") + with self.assertRaises(rsa.DecryptionError): + rsa.decrypt(cyphertext, self.private_key) + + +class SignatureTest(unittest.TestCase): + def setUp(self): + (self.pub, self.priv) = rsa.newkeys(512) + + def test_sign_verify(self): + """Test happy flow of sign and verify""" + + message = b"je moeder" + signature = pkcs1.sign(message, self.priv, "SHA-256") + self.assertEqual("SHA-256", pkcs1.verify(message, signature, self.pub)) + + @unittest.skipIf(sys.version_info < (3, 6), "SHA3 requires Python 3.6+") + def test_sign_verify_sha3(self): + """Test happy flow of sign and verify with SHA3-256""" + + message = b"je moeder" + signature = pkcs1.sign(message, self.priv, "SHA3-256") + self.assertEqual("SHA3-256", pkcs1.verify(message, signature, self.pub)) + + def test_find_signature_hash(self): + """Test happy flow of sign and find_signature_hash""" + + message = b"je moeder" + signature = pkcs1.sign(message, self.priv, "SHA-256") + + self.assertEqual("SHA-256", pkcs1.find_signature_hash(signature, self.pub)) + + def test_alter_message(self): + """Altering the message should let the verification fail.""" + + signature = pkcs1.sign(b"je moeder", self.priv, "SHA-256") + self.assertRaises( + pkcs1.VerificationError, pkcs1.verify, b"mijn moeder", signature, self.pub + ) + + def test_sign_different_key(self): + """Signing with another key should let the verification fail.""" + + (otherpub, _) = rsa.newkeys(512) + + message = b"je moeder" + signature = pkcs1.sign(message, self.priv, "SHA-256") + self.assertRaises(pkcs1.VerificationError, pkcs1.verify, message, signature, otherpub) + + def test_multiple_signings(self): + """Signing the same message twice should return the same signatures.""" + + message = struct.pack(">IIII", 0, 0, 0, 1) + signature1 = pkcs1.sign(message, self.priv, "SHA-1") + signature2 = pkcs1.sign(message, self.priv, "SHA-1") + + self.assertEqual(signature1, signature2) + + def test_split_hash_sign(self): + """Hashing and then signing should match with directly signing the message.""" + + message = b"je moeder" + msg_hash = pkcs1.compute_hash(message, "SHA-256") + signature1 = pkcs1.sign_hash(msg_hash, self.priv, "SHA-256") + + # Calculate the signature using the unified method + signature2 = pkcs1.sign(message, self.priv, "SHA-256") + + self.assertEqual(signature1, signature2) + + def test_hash_sign_verify(self): + """Test happy flow of hash, sign, and verify""" + + message = b"je moeder" + msg_hash = pkcs1.compute_hash(message, "SHA-224") + signature = pkcs1.sign_hash(msg_hash, self.priv, "SHA-224") + + self.assertTrue(pkcs1.verify(message, signature, self.pub)) + + def test_prepend_zeroes(self): + """Prepending the signature with zeroes should be detected.""" + + message = b"je moeder" + signature = pkcs1.sign(message, self.priv, "SHA-256") + signature = bytes.fromhex("0000") + signature + with self.assertRaises(rsa.VerificationError): + pkcs1.verify(message, signature, self.pub) + + def test_apppend_zeroes(self): + """Apppending the signature with zeroes should be detected.""" + + message = b"je moeder" + signature = pkcs1.sign(message, self.priv, "SHA-256") + signature = signature + bytes.fromhex("0000") + with self.assertRaises(rsa.VerificationError): + pkcs1.verify(message, signature, self.pub) + + +class PaddingSizeTest(unittest.TestCase): + def test_too_little_padding(self): + """Padding less than 8 bytes should be rejected.""" + + # Construct key that will be small enough to need only 7 bytes of padding. + # This key is 168 bit long, and was generated with rsa.newkeys(nbits=168). + self.private_key = rsa.PrivateKey.load_pkcs1( + b""" +-----BEGIN RSA PRIVATE KEY----- +MHkCAQACFgCIGbbNSkIRLtprxka9NgOf5UxgxCMCAwEAAQIVQqymO0gHubdEVS68 +CdCiWmOJxVfRAgwBQM+e1JJwMKmxSF0CCmya6CFxO8Evdn8CDACMM3AlVC4FhlN8 +3QIKC9cjoam/swMirwIMAR7Br9tdouoH7jAE +-----END RSA PRIVATE KEY----- + """ + ) + self.public_key = rsa.PublicKey(n=self.private_key.n, e=self.private_key.e) + + cyphertext = self.encrypt_with_short_padding(b"op je hoofd") + with self.assertRaises(rsa.DecryptionError): + rsa.decrypt(cyphertext, self.private_key) + + def encrypt_with_short_padding(self, message: bytes) -> bytes: + # This is a copy of rsa.pkcs1.encrypt() adjusted to use the wrong padding length. + keylength = rsa.common.byte_size(self.public_key.n) + + # The word 'padding' has 7 letters, so is one byte short of a valid padding length. + padded = b"\x00\x02padding\x00" + message + + payload = rsa.transform.bytes2int(padded) + encrypted_value = rsa.core.encrypt_int(payload, self.public_key.e, self.public_key.n) + cyphertext = rsa.transform.int2bytes(encrypted_value, keylength) + + return cyphertext diff --git a/contrib/python/rsa/py3/tests/test_pkcs1_v2.py b/contrib/python/rsa/py3/tests/test_pkcs1_v2.py new file mode 100644 index 0000000000..ead1393fe1 --- /dev/null +++ b/contrib/python/rsa/py3/tests/test_pkcs1_v2.py @@ -0,0 +1,79 @@ +# Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests PKCS #1 version 2 functionality. + +Most of the mocked values come from the test vectors found at: +http://www.itomorrowmag.com/emc-plus/rsa-labs/standards-initiatives/pkcs-rsa-cryptography-standard.htm +""" + +import unittest + +from rsa import pkcs1_v2 + + +class MGFTest(unittest.TestCase): + def test_oaep_int_db_mask(self): + seed = ( + b"\xaa\xfd\x12\xf6\x59\xca\xe6\x34\x89\xb4\x79\xe5\x07\x6d\xde\xc2" b"\xf0\x6c\xb5\x8f" + ) + db = ( + b"\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18\x90" + b"\xaf\xd8\x07\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xd4\x36\xe9\x95\x69" + b"\xfd\x32\xa7\xc8\xa0\x5b\xbc\x90\xd3\x2c\x49" + ) + masked_db = ( + b"\xdc\xd8\x7d\x5c\x68\xf1\xee\xa8\xf5\x52\x67\xc3\x1b\x2e\x8b\xb4" + b"\x25\x1f\x84\xd7\xe0\xb2\xc0\x46\x26\xf5\xaf\xf9\x3e\xdc\xfb\x25" + b"\xc9\xc2\xb3\xff\x8a\xe1\x0e\x83\x9a\x2d\xdb\x4c\xdc\xfe\x4f\xf4" + b"\x77\x28\xb4\xa1\xb7\xc1\x36\x2b\xaa\xd2\x9a\xb4\x8d\x28\x69\xd5" + b"\x02\x41\x21\x43\x58\x11\x59\x1b\xe3\x92\xf9\x82\xfb\x3e\x87\xd0" + b"\x95\xae\xb4\x04\x48\xdb\x97\x2f\x3a\xc1\x4f\x7b\xc2\x75\x19\x52" + b"\x81\xce\x32\xd2\xf1\xb7\x6d\x4d\x35\x3e\x2d" + ) + + # dbMask = MGF(seed, length(DB)) + db_mask = pkcs1_v2.mgf1(seed, length=len(db)) + expected_db_mask = ( + b"\x06\xe1\xde\xb2\x36\x9a\xa5\xa5\xc7\x07\xd8\x2c\x8e\x4e\x93\x24" + b"\x8a\xc7\x83\xde\xe0\xb2\xc0\x46\x26\xf5\xaf\xf9\x3e\xdc\xfb\x25" + b"\xc9\xc2\xb3\xff\x8a\xe1\x0e\x83\x9a\x2d\xdb\x4c\xdc\xfe\x4f\xf4" + b"\x77\x28\xb4\xa1\xb7\xc1\x36\x2b\xaa\xd2\x9a\xb4\x8d\x28\x69\xd5" + b"\x02\x41\x21\x43\x58\x11\x59\x1b\xe3\x92\xf9\x82\xfb\x3e\x87\xd0" + b"\x95\xae\xb4\x04\x48\xdb\x97\x2f\x3a\xc1\x4e\xaf\xf4\x9c\x8c\x3b" + b"\x7c\xfc\x95\x1a\x51\xec\xd1\xdd\xe6\x12\x64" + ) + + self.assertEqual(db_mask, expected_db_mask) + + # seedMask = MGF(maskedDB, length(seed)) + seed_mask = pkcs1_v2.mgf1(masked_db, length=len(seed)) + expected_seed_mask = ( + b"\x41\x87\x0b\x5a\xb0\x29\xe6\x57\xd9\x57\x50\xb5\x4c\x28\x3c\x08" b"\x72\x5d\xbe\xa9" + ) + + self.assertEqual(seed_mask, expected_seed_mask) + + def test_invalid_hasher(self): + """Tests an invalid hasher generates an exception""" + with self.assertRaises(ValueError): + pkcs1_v2.mgf1(b"\x06\xe1\xde\xb2", length=8, hasher="SHA2") + + def test_invalid_length(self): + with self.assertRaises(OverflowError): + pkcs1_v2.mgf1(b"\x06\xe1\xde\xb2", length=2 ** 50) diff --git a/contrib/python/rsa/py3/tests/test_prime.py b/contrib/python/rsa/py3/tests/test_prime.py new file mode 100644 index 0000000000..42d8af1670 --- /dev/null +++ b/contrib/python/rsa/py3/tests/test_prime.py @@ -0,0 +1,133 @@ +# Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests prime functions.""" + +import unittest + +import rsa.prime +import rsa.randnum + + +class PrimeTest(unittest.TestCase): + def test_is_prime(self): + """Test some common primes.""" + + # Test some trivial numbers + self.assertFalse(rsa.prime.is_prime(-1)) + self.assertFalse(rsa.prime.is_prime(0)) + self.assertFalse(rsa.prime.is_prime(1)) + self.assertTrue(rsa.prime.is_prime(2)) + self.assertFalse(rsa.prime.is_prime(42)) + self.assertTrue(rsa.prime.is_prime(41)) + + # Test some slightly larger numbers + self.assertEqual( + [907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997], + [x for x in range(901, 1000) if rsa.prime.is_prime(x)], + ) + + # Test around the 50th millionth known prime. + self.assertTrue(rsa.prime.is_prime(982451653)) + self.assertFalse(rsa.prime.is_prime(982451653 * 961748941)) + + def test_miller_rabin_primality_testing(self): + """Uses monkeypatching to ensure certain random numbers. + + This allows us to predict/control the code path. + """ + + randints = [] + + def fake_randint(maxvalue): + return randints.pop(0) + + orig_randint = rsa.randnum.randint + rsa.randnum.randint = fake_randint + try: + # 'n is composite' + randints.append(2630484832) # causes the 'n is composite' case with n=3784949785 + self.assertEqual(False, rsa.prime.miller_rabin_primality_testing(2787998641, 7)) + self.assertEqual([], randints) + + # 'Exit inner loop and continue with next witness' + randints.extend( + [ + 2119139098, # causes 'Exit inner loop and continue with next witness' + # the next witnesses for the above case: + 3051067716, + 3603501763, + 3230895847, + 3687808133, + 3760099987, + 4026931495, + 3022471882, + ] + ) + self.assertEqual( + True, + rsa.prime.miller_rabin_primality_testing(2211417913, len(randints)), + ) + self.assertEqual([], randints) + finally: + rsa.randnum.randint = orig_randint + + def test_mersenne_primes(self): + """Tests first known Mersenne primes. + + Mersenne primes are prime numbers that can be written in the form + `Mn = 2**n - 1` for some integer `n`. For the list of known Mersenne + primes, see: + https://en.wikipedia.org/wiki/Mersenne_prime#List_of_known_Mersenne_primes + """ + + # List of known Mersenne exponents. + known_mersenne_exponents = [ + 2, + 3, + 5, + 7, + 13, + 17, + 19, + 31, + 61, + 89, + 107, + 127, + 521, + 607, + 1279, + 2203, + 2281, + 4423, + ] + + # Test Mersenne primes. + for exp in known_mersenne_exponents: + self.assertTrue(rsa.prime.is_prime(2 ** exp - 1)) + + def test_get_primality_testing_rounds(self): + """Test round calculation for primality testing.""" + + self.assertEqual(rsa.prime.get_primality_testing_rounds(1 << 63), 10) + self.assertEqual(rsa.prime.get_primality_testing_rounds(1 << 127), 10) + self.assertEqual(rsa.prime.get_primality_testing_rounds(1 << 255), 10) + self.assertEqual(rsa.prime.get_primality_testing_rounds(1 << 511), 7) + self.assertEqual(rsa.prime.get_primality_testing_rounds(1 << 767), 7) + self.assertEqual(rsa.prime.get_primality_testing_rounds(1 << 1023), 4) + self.assertEqual(rsa.prime.get_primality_testing_rounds(1 << 1279), 4) + self.assertEqual(rsa.prime.get_primality_testing_rounds(1 << 1535), 3) + self.assertEqual(rsa.prime.get_primality_testing_rounds(1 << 2047), 3) + self.assertEqual(rsa.prime.get_primality_testing_rounds(1 << 4095), 3) diff --git a/contrib/python/rsa/py3/tests/test_strings.py b/contrib/python/rsa/py3/tests/test_strings.py new file mode 100644 index 0000000000..ae8ffe1a4e --- /dev/null +++ b/contrib/python/rsa/py3/tests/test_strings.py @@ -0,0 +1,40 @@ +# Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests string operations.""" + +from __future__ import absolute_import + +import unittest + +import rsa + +unicode_string = u"Euro=\u20ac ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + +class StringTest(unittest.TestCase): + def setUp(self): + (self.pub, self.priv) = rsa.newkeys(384) + + def test_enc_dec(self): + message = unicode_string.encode("utf-8") + print("\n\tMessage: %r" % message) + + encrypted = rsa.encrypt(message, self.pub) + print("\tEncrypted: %r" % encrypted) + + decrypted = rsa.decrypt(encrypted, self.priv) + print("\tDecrypted: %r" % decrypted) + + self.assertEqual(message, decrypted) diff --git a/contrib/python/rsa/py3/tests/test_transform.py b/contrib/python/rsa/py3/tests/test_transform.py new file mode 100644 index 0000000000..14046191fe --- /dev/null +++ b/contrib/python/rsa/py3/tests/test_transform.py @@ -0,0 +1,53 @@ +# Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +from rsa.transform import int2bytes, bytes2int + + +class Test_int2bytes(unittest.TestCase): + def test_accuracy(self): + self.assertEqual(int2bytes(123456789), b"\x07[\xcd\x15") + + def test_codec_identity(self): + self.assertEqual(bytes2int(int2bytes(123456789, 128)), 123456789) + + def test_chunk_size(self): + self.assertEqual(int2bytes(123456789, 6), b"\x00\x00\x07[\xcd\x15") + self.assertEqual(int2bytes(123456789, 7), b"\x00\x00\x00\x07[\xcd\x15") + + def test_zero(self): + self.assertEqual(int2bytes(0, 4), b"\x00" * 4) + self.assertEqual(int2bytes(0, 7), b"\x00" * 7) + self.assertEqual(int2bytes(0), b"\x00") + + def test_correctness_against_base_implementation(self): + # Slow test. + values = [ + 1 << 512, + 1 << 8192, + 1 << 77, + ] + for value in values: + self.assertEqual(bytes2int(int2bytes(value)), value, "Boom %d" % value) + + def test_raises_OverflowError_when_chunk_size_is_insufficient(self): + self.assertRaises(OverflowError, int2bytes, 123456789, 3) + self.assertRaises(OverflowError, int2bytes, 299999999999, 4) + + def test_raises_ValueError_when_negative_integer(self): + self.assertRaises(ValueError, int2bytes, -1) + + def test_raises_TypeError_when_not_integer(self): + self.assertRaises(TypeError, int2bytes, None) diff --git a/contrib/python/rsa/py3/tests/ya.make b/contrib/python/rsa/py3/tests/ya.make new file mode 100644 index 0000000000..059fbe0129 --- /dev/null +++ b/contrib/python/rsa/py3/tests/ya.make @@ -0,0 +1,28 @@ +PY3TEST() + +PEERDIR( + contrib/python/rsa +) + +NO_LINT() + +TEST_SRCS( + test_cli.py + test_common.py + test_integers.py + test_key.py + test_load_save_keys.py + test_parallel.py + test_pem.py + test_pkcs1.py + test_pkcs1_v2.py + test_prime.py + test_strings.py + test_transform.py +) + +DATA ( + arcadia/contrib/python/rsa/py3/tests +) + +END() |