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/py2/tests | |
parent | 84f2d3d4cc985e63217cff149bd2e6d67ae6fe22 (diff) | |
download | ydb-0e578a4c44d4abd539d9838347b9ebafaca41dfb.tar.gz |
Change "ya.make"
Diffstat (limited to 'contrib/python/rsa/py2/tests')
-rw-r--r-- | contrib/python/rsa/py2/tests/__init__.py | 0 | ||||
-rw-r--r-- | contrib/python/rsa/py2/tests/private.pem | 5 | ||||
-rw-r--r-- | contrib/python/rsa/py2/tests/test_cli.py | 296 | ||||
-rw-r--r-- | contrib/python/rsa/py2/tests/test_common.py | 96 | ||||
-rw-r--r-- | contrib/python/rsa/py2/tests/test_compat.py | 80 | ||||
-rw-r--r-- | contrib/python/rsa/py2/tests/test_integers.py | 50 | ||||
-rw-r--r-- | contrib/python/rsa/py2/tests/test_key.py | 79 | ||||
-rw-r--r-- | contrib/python/rsa/py2/tests/test_load_save_keys.py | 217 | ||||
-rw-r--r-- | contrib/python/rsa/py2/tests/test_parallel.py | 20 | ||||
-rw-r--r-- | contrib/python/rsa/py2/tests/test_pem.py | 102 | ||||
-rw-r--r-- | contrib/python/rsa/py2/tests/test_pkcs1.py | 184 | ||||
-rw-r--r-- | contrib/python/rsa/py2/tests/test_pkcs1_v2.py | 83 | ||||
-rw-r--r-- | contrib/python/rsa/py2/tests/test_prime.py | 110 | ||||
-rw-r--r-- | contrib/python/rsa/py2/tests/test_strings.py | 42 | ||||
-rw-r--r-- | contrib/python/rsa/py2/tests/test_transform.py | 79 | ||||
-rw-r--r-- | contrib/python/rsa/py2/tests/ya.make | 30 |
16 files changed, 1473 insertions, 0 deletions
diff --git a/contrib/python/rsa/py2/tests/__init__.py b/contrib/python/rsa/py2/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/contrib/python/rsa/py2/tests/__init__.py diff --git a/contrib/python/rsa/py2/tests/private.pem b/contrib/python/rsa/py2/tests/private.pem new file mode 100644 index 0000000000..1a17279f23 --- /dev/null +++ b/contrib/python/rsa/py2/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/py2/tests/test_cli.py b/contrib/python/rsa/py2/tests/test_cli.py new file mode 100644 index 0000000000..7ce57ebd99 --- /dev/null +++ b/contrib/python/rsa/py2/tests/test_cli.py @@ -0,0 +1,296 @@ +""" +Unit tests for CLI entry points. +""" + +from __future__ import print_function + +import unittest +import sys +import functools +from contextlib import contextmanager + +import os +from io import StringIO, BytesIO + +import rsa +import rsa.cli +import rsa.util +from rsa._compat import PY2 + + +def make_buffer(): + if PY2: + return BytesIO() + buf = StringIO() + buf.buffer = BytesIO() + return buf + + +def get_bytes_out(out): + if PY2: + # Python 2.x writes 'str' to stdout + return out.getvalue() + # Python 3.x writes 'bytes' to stdout.buffer + return out.buffer.getvalue() + + +@contextmanager +def captured_output(): + """Captures output to stdout and stderr""" + + new_out, new_err = make_buffer(), make_buffer() + old_out, old_err = sys.stdout, sys.stderr + try: + sys.stdout, sys.stderr = new_out, new_err + yield new_out, new_err + finally: + sys.stdout, sys.stderr = old_out, old_err + + +@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 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 cli_args(): + self.assertExits(1, rsa.cli.decrypt) + + def test_empty_encrypt(self): + with 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 cli_args(): + self.assertExits(1, rsa.cli.verify) + + def test_empty_sign(self): + with 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/py2/tests/test_common.py b/contrib/python/rsa/py2/tests/test_common.py new file mode 100644 index 0000000000..af13695a7e --- /dev/null +++ b/contrib/python/rsa/py2/tests/test_common.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# 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._compat import byte +from rsa.common import byte_size, bit_size, inverse + + +class TestByte(unittest.TestCase): + def test_values(self): + self.assertEqual(byte(0), b'\x00') + self.assertEqual(byte(255), b'\xff') + + def test_struct_error_when_out_of_bounds(self): + self.assertRaises(struct.error, byte, 256) + self.assertRaises(struct.error, byte, -1) + + +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/py2/tests/test_compat.py b/contrib/python/rsa/py2/tests/test_compat.py new file mode 100644 index 0000000000..62e933f25f --- /dev/null +++ b/contrib/python/rsa/py2/tests/test_compat.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +# +# 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._compat import byte, is_bytes, range, xor_bytes + + +class TestByte(unittest.TestCase): + """Tests for single bytes.""" + + def test_byte(self): + for i in range(256): + byt = byte(i) + self.assertTrue(is_bytes(byt)) + self.assertEqual(ord(byt), i) + + def test_raises_StructError_on_overflow(self): + self.assertRaises(struct.error, byte, 256) + self.assertRaises(struct.error, byte, -1) + + def test_byte_literal(self): + self.assertIsInstance(b'abc', bytes) + + +class TestBytes(unittest.TestCase): + """Tests for bytes objects.""" + + def setUp(self): + self.b1 = b'\xff\xff\xff\xff' + self.b2 = b'\x00\x00\x00\x00' + self.b3 = b'\xf0\xf0\xf0\xf0' + self.b4 = b'\x4d\x23\xca\xe2' + self.b5 = b'\x9b\x61\x3b\xdc' + self.b6 = b'\xff\xff' + + self.byte_strings = (self.b1, self.b2, self.b3, self.b4, self.b5, self.b6) + + def test_xor_bytes(self): + self.assertEqual(xor_bytes(self.b1, self.b2), b'\xff\xff\xff\xff') + self.assertEqual(xor_bytes(self.b1, self.b3), b'\x0f\x0f\x0f\x0f') + self.assertEqual(xor_bytes(self.b1, self.b4), b'\xb2\xdc\x35\x1d') + self.assertEqual(xor_bytes(self.b1, self.b5), b'\x64\x9e\xc4\x23') + self.assertEqual(xor_bytes(self.b2, self.b3), b'\xf0\xf0\xf0\xf0') + self.assertEqual(xor_bytes(self.b2, self.b4), b'\x4d\x23\xca\xe2') + self.assertEqual(xor_bytes(self.b2, self.b5), b'\x9b\x61\x3b\xdc') + self.assertEqual(xor_bytes(self.b3, self.b4), b'\xbd\xd3\x3a\x12') + self.assertEqual(xor_bytes(self.b3, self.b5), b'\x6b\x91\xcb\x2c') + self.assertEqual(xor_bytes(self.b4, self.b5), b'\xd6\x42\xf1\x3e') + + def test_xor_bytes_length(self): + self.assertEqual(xor_bytes(self.b1, self.b6), b'\x00\x00') + self.assertEqual(xor_bytes(self.b2, self.b6), b'\xff\xff') + self.assertEqual(xor_bytes(self.b3, self.b6), b'\x0f\x0f') + self.assertEqual(xor_bytes(self.b4, self.b6), b'\xb2\xdc') + self.assertEqual(xor_bytes(self.b5, self.b6), b'\x64\x9e') + self.assertEqual(xor_bytes(self.b6, b''), b'') + + def test_xor_bytes_commutative(self): + for first in self.byte_strings: + for second in self.byte_strings: + min_length = min(len(first), len(second)) + result = xor_bytes(first, second) + + self.assertEqual(result, xor_bytes(second, first)) + self.assertEqual(len(result), min_length) diff --git a/contrib/python/rsa/py2/tests/test_integers.py b/contrib/python/rsa/py2/tests/test_integers.py new file mode 100644 index 0000000000..fb29ba41d3 --- /dev/null +++ b/contrib/python/rsa/py2/tests/test_integers.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# +# 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("\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("\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/py2/tests/test_key.py b/contrib/python/rsa/py2/tests/test_key.py new file mode 100644 index 0000000000..9db30cedf6 --- /dev/null +++ b/contrib/python/rsa/py2/tests/test_key.py @@ -0,0 +1,79 @@ +""" +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 = pk.blind(encrypted, 4134431) # blind before decrypting + decrypted = rsa.core.decrypt_int(blinded, pk.d, pk.n) + unblinded = pk.unblind(decrypted, 4134431) + + self.assertEqual(unblinded, message) + + +class KeyGenTest(unittest.TestCase): + def test_custom_exponent(self): + priv, pub = rsa.key.newkeys(16, exponent=3) + + self.assertEqual(3, priv.e) + self.assertEqual(3, pub.e) + + def test_default_exponent(self): + priv, pub = 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): + priv, pub = rsa.key.newkeys(16) + + # This raises a TypeError when hashing isn't possible. + hash(priv) + hash(pub) diff --git a/contrib/python/rsa/py2/tests/test_load_save_keys.py b/contrib/python/rsa/py2/tests/test_load_save_keys.py new file mode 100644 index 0000000000..967c946e74 --- /dev/null +++ b/contrib/python/rsa/py2/tests/test_load_save_keys.py @@ -0,0 +1,217 @@ +# -*- coding: utf-8 -*- +# +# 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 mock +import os.path +import pickle +import unittest +import warnings + +from rsa._compat import range +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): + from yatest.common import source_path + + """Test loading a PEM file from disk.""" + + fname = source_path('contrib/python/rsa/py2/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) + + def test_public_key(self): + pk = rsa.key.PublicKey(3727264081, 65537) + + pickled = pickle.dumps(pk) + unpickled = pickle.loads(pickled) + + self.assertEqual(pk, unpickled) diff --git a/contrib/python/rsa/py2/tests/test_parallel.py b/contrib/python/rsa/py2/tests/test_parallel.py new file mode 100644 index 0000000000..1a69e9ece6 --- /dev/null +++ b/contrib/python/rsa/py2/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/py2/tests/test_pem.py b/contrib/python/rsa/py2/tests/test_pem.py new file mode 100644 index 0000000000..5fb96002af --- /dev/null +++ b/contrib/python/rsa/py2/tests/test_pem.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# 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._compat import is_bytes +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.assertTrue(is_bytes(key.save_pkcs1(format='DER'))) + self.assertTrue(is_bytes(key.save_pkcs1(format='PEM'))) + + def test_bytes_private(self): + key = rsa.key.PrivateKey.load_pkcs1(private_key_pem) + self.assertTrue(is_bytes(key.save_pkcs1(format='DER'))) + self.assertTrue(is_bytes(key.save_pkcs1(format='PEM'))) + + +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.assertTrue(is_bytes(key.save_pkcs1(format='DER'))) + self.assertTrue(is_bytes(key.save_pkcs1(format='PEM'))) + + def test_bytes_private(self): + key = rsa.key.PrivateKey.load_pkcs1(private_key_pem.encode('ascii')) + self.assertTrue(is_bytes(key.save_pkcs1(format='DER'))) + self.assertTrue(is_bytes(key.save_pkcs1(format='PEM'))) diff --git a/contrib/python/rsa/py2/tests/test_pkcs1.py b/contrib/python/rsa/py2/tests/test_pkcs1.py new file mode 100644 index 0000000000..9f7dcea7ad --- /dev/null +++ b/contrib/python/rsa/py2/tests/test_pkcs1.py @@ -0,0 +1,184 @@ +# -*- coding: utf-8 -*- +# +# 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 +from rsa._compat import byte, is_bytes + + +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("\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] + if is_bytes(a): + a = ord(a) + altered_a = (a + 1) % 256 + encrypted = encrypted[:5] + byte(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') + cyphertext = "4501b4d669e01b9ef2dc800aa1b06d49196f5a09fe8fbcd037323c60eaf027bfb98432be4e4a26c567ffec718bcbea977dd26812fa071c33808b4d5ebb742d9879806094b6fbeea63d25ea3141733b60e31c6912106e1b758a7fe0014f075193faa8b4622bfd5d3013f0a32190a95de61a3604711bc62945f95a6522bd4dfed0a994ef185b28c281f7b5e4c8ed41176d12d9fc1b837e6a0111d0132d08a6d6f0580de0c9eed8ed105531799482d1e466c68c23b0c222af7fc12ac279bc4ff57e7b4586d209371b38c4c1035edd418dc5f960441cb21ea2bedbfea86de0d7861e81021b650a1de51002c315f1e7c12debe4dcebf790caaa54a2f26b149cf9e77d" + plaintext = "54657374" + + if sys.version_info < (3, 0): + self.cyphertext = cyphertext.decode("hex") + self.plaintext = plaintext.decode('hex') + else: + self.cyphertext = bytes.fromhex(cyphertext) + self.plaintext = bytes.fromhex(plaintext) + + def test_unmodified(self): + message = rsa.decrypt(self.cyphertext, self.private_key) + self.assertEqual(message, self.plaintext) + + def test_prepend_zeroes(self): + cyphertext = b'\00\00' + self.cyphertext + with self.assertRaises(rsa.DecryptionError): + rsa.decrypt(cyphertext, self.private_key) + + def test_append_zeroes(self): + cyphertext = self.cyphertext + b'\00\00' + 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)) + + 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 = b'\00\00' + 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 + b'\00\00' + with self.assertRaises(rsa.VerificationError): + pkcs1.verify(message, signature, self.pub) diff --git a/contrib/python/rsa/py2/tests/test_pkcs1_v2.py b/contrib/python/rsa/py2/tests/test_pkcs1_v2.py new file mode 100644 index 0000000000..1d8f0010de --- /dev/null +++ b/contrib/python/rsa/py2/tests/test_pkcs1_v2.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +# +# 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/py2/tests/test_prime.py b/contrib/python/rsa/py2/tests/test_prime.py new file mode 100644 index 0000000000..f3bda9b486 --- /dev/null +++ b/contrib/python/rsa/py2/tests/test_prime.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +# +# 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 + +from rsa._compat import range +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/py2/tests/test_strings.py b/contrib/python/rsa/py2/tests/test_strings.py new file mode 100644 index 0000000000..28fa091a47 --- /dev/null +++ b/contrib/python/rsa/py2/tests/test_strings.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# +# 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("\tMessage: %s" % message) + + encrypted = rsa.encrypt(message, self.pub) + print("\tEncrypted: %s" % encrypted) + + decrypted = rsa.decrypt(encrypted, self.priv) + print("\tDecrypted: %s" % decrypted) + + self.assertEqual(message, decrypted) diff --git a/contrib/python/rsa/py2/tests/test_transform.py b/contrib/python/rsa/py2/tests/test_transform.py new file mode 100644 index 0000000000..fe0970c962 --- /dev/null +++ b/contrib/python/rsa/py2/tests/test_transform.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +# +# 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, _int2bytes + + +class Test_int2bytes(unittest.TestCase): + def test_accuracy(self): + self.assertEqual(int2bytes(123456789), b'\x07[\xcd\x15') + self.assertEqual(_int2bytes(123456789), b'\x07[\xcd\x15') + + def test_codec_identity(self): + self.assertEqual(bytes2int(int2bytes(123456789, 128)), 123456789) + 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') + + 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') + + 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(int2bytes(value), _int2bytes(value), + "Boom %d" % value) + self.assertEqual(bytes2int(int2bytes(value)), + value, + "Boom %d" % value) + 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) + + 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) + self.assertRaises(ValueError, _int2bytes, -1) + + def test_raises_TypeError_when_not_integer(self): + self.assertRaises(TypeError, int2bytes, None) + self.assertRaises(TypeError, _int2bytes, None) diff --git a/contrib/python/rsa/py2/tests/ya.make b/contrib/python/rsa/py2/tests/ya.make new file mode 100644 index 0000000000..05640300fb --- /dev/null +++ b/contrib/python/rsa/py2/tests/ya.make @@ -0,0 +1,30 @@ +PY2TEST() + +PEERDIR( + contrib/python/rsa + contrib/python/mock +) + +NO_LINT() + +TEST_SRCS( + test_cli.py + test_common.py + test_compat.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/py2/tests +) + +END() |