aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/rsa/py3/tests
diff options
context:
space:
mode:
authoralexv-smirnov <alex@ydb.tech>2023-12-01 12:02:50 +0300
committeralexv-smirnov <alex@ydb.tech>2023-12-01 13:28:10 +0300
commit0e578a4c44d4abd539d9838347b9ebafaca41dfb (patch)
treea0c1969c37f818c830ebeff9c077eacf30be6ef8 /contrib/python/rsa/py3/tests
parent84f2d3d4cc985e63217cff149bd2e6d67ae6fe22 (diff)
downloadydb-0e578a4c44d4abd539d9838347b9ebafaca41dfb.tar.gz
Change "ya.make"
Diffstat (limited to 'contrib/python/rsa/py3/tests')
-rw-r--r--contrib/python/rsa/py3/tests/__init__.py0
-rw-r--r--contrib/python/rsa/py3/tests/private.pem5
-rw-r--r--contrib/python/rsa/py3/tests/test_cli.py291
-rw-r--r--contrib/python/rsa/py3/tests/test_common.py83
-rw-r--r--contrib/python/rsa/py3/tests/test_integers.py48
-rw-r--r--contrib/python/rsa/py3/tests/test_key.py87
-rw-r--r--contrib/python/rsa/py3/tests/test_load_save_keys.py234
-rw-r--r--contrib/python/rsa/py3/tests/test_mypy.py31
-rw-r--r--contrib/python/rsa/py3/tests/test_parallel.py20
-rw-r--r--contrib/python/rsa/py3/tests/test_pem.py100
-rw-r--r--contrib/python/rsa/py3/tests/test_pkcs1.py218
-rw-r--r--contrib/python/rsa/py3/tests/test_pkcs1_v2.py79
-rw-r--r--contrib/python/rsa/py3/tests/test_prime.py133
-rw-r--r--contrib/python/rsa/py3/tests/test_strings.py40
-rw-r--r--contrib/python/rsa/py3/tests/test_transform.py53
-rw-r--r--contrib/python/rsa/py3/tests/ya.make28
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()