aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/rsa/py2
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/py2
parent84f2d3d4cc985e63217cff149bd2e6d67ae6fe22 (diff)
downloadydb-0e578a4c44d4abd539d9838347b9ebafaca41dfb.tar.gz
Change "ya.make"
Diffstat (limited to 'contrib/python/rsa/py2')
-rw-r--r--contrib/python/rsa/py2/.dist-info/METADATA85
-rw-r--r--contrib/python/rsa/py2/.dist-info/entry_points.txt8
-rw-r--r--contrib/python/rsa/py2/.dist-info/top_level.txt1
-rw-r--r--contrib/python/rsa/py2/LICENSE13
-rw-r--r--contrib/python/rsa/py2/README.md52
-rw-r--r--contrib/python/rsa/py2/rsa/__init__.py42
-rw-r--r--contrib/python/rsa/py2/rsa/_compat.py162
-rw-r--r--contrib/python/rsa/py2/rsa/asn1.py53
-rw-r--r--contrib/python/rsa/py2/rsa/cli.py288
-rw-r--r--contrib/python/rsa/py2/rsa/common.py188
-rw-r--r--contrib/python/rsa/py2/rsa/core.py57
-rw-r--r--contrib/python/rsa/py2/rsa/key.py798
-rw-r--r--contrib/python/rsa/py2/rsa/machine_size.py74
-rw-r--r--contrib/python/rsa/py2/rsa/parallel.py101
-rw-r--r--contrib/python/rsa/py2/rsa/pem.py126
-rw-r--r--contrib/python/rsa/py2/rsa/pkcs1.py448
-rw-r--r--contrib/python/rsa/py2/rsa/pkcs1_v2.py103
-rw-r--r--contrib/python/rsa/py2/rsa/prime.py201
-rw-r--r--contrib/python/rsa/py2/rsa/randnum.py98
-rw-r--r--contrib/python/rsa/py2/rsa/transform.py215
-rw-r--r--contrib/python/rsa/py2/rsa/util.py79
-rw-r--r--contrib/python/rsa/py2/tests/__init__.py0
-rw-r--r--contrib/python/rsa/py2/tests/private.pem5
-rw-r--r--contrib/python/rsa/py2/tests/test_cli.py296
-rw-r--r--contrib/python/rsa/py2/tests/test_common.py96
-rw-r--r--contrib/python/rsa/py2/tests/test_compat.py80
-rw-r--r--contrib/python/rsa/py2/tests/test_integers.py50
-rw-r--r--contrib/python/rsa/py2/tests/test_key.py79
-rw-r--r--contrib/python/rsa/py2/tests/test_load_save_keys.py217
-rw-r--r--contrib/python/rsa/py2/tests/test_parallel.py20
-rw-r--r--contrib/python/rsa/py2/tests/test_pem.py102
-rw-r--r--contrib/python/rsa/py2/tests/test_pkcs1.py184
-rw-r--r--contrib/python/rsa/py2/tests/test_pkcs1_v2.py83
-rw-r--r--contrib/python/rsa/py2/tests/test_prime.py110
-rw-r--r--contrib/python/rsa/py2/tests/test_strings.py42
-rw-r--r--contrib/python/rsa/py2/tests/test_transform.py79
-rw-r--r--contrib/python/rsa/py2/tests/ya.make30
-rw-r--r--contrib/python/rsa/py2/ya.make46
38 files changed, 4711 insertions, 0 deletions
diff --git a/contrib/python/rsa/py2/.dist-info/METADATA b/contrib/python/rsa/py2/.dist-info/METADATA
new file mode 100644
index 0000000000..3ab66353aa
--- /dev/null
+++ b/contrib/python/rsa/py2/.dist-info/METADATA
@@ -0,0 +1,85 @@
+Metadata-Version: 2.1
+Name: rsa
+Version: 4.5
+Summary: Pure-Python RSA implementation
+Home-page: https://stuvel.eu/rsa
+Author: Sybren A. Stuvel
+Author-email: sybren@stuvel.eu
+Maintainer: Sybren A. Stuvel
+Maintainer-email: sybren@stuvel.eu
+License: ASL 2
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Education
+Classifier: Intended Audience :: Information Technology
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Security :: Cryptography
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4
+Description-Content-Type: text/markdown
+Requires-Dist: pyasn1 (>=0.1.3)
+
+Pure Python RSA implementation
+==============================
+
+[![PyPI](https://img.shields.io/pypi/v/rsa.svg)](https://pypi.org/project/rsa/)
+[![Build Status](https://travis-ci.org/sybrenstuvel/python-rsa.svg?branch=master)](https://travis-ci.org/sybrenstuvel/python-rsa)
+[![Coverage Status](https://coveralls.io/repos/github/sybrenstuvel/python-rsa/badge.svg?branch=master)](https://coveralls.io/github/sybrenstuvel/python-rsa?branch=master)
+[![Code Climate](https://img.shields.io/codeclimate/github/sybrenstuvel/python-rsa.svg)](https://codeclimate.com/github/sybrenstuvel/python-rsa)
+
+[Python-RSA](https://stuvel.eu/rsa) is a pure-Python RSA implementation. It supports
+encryption and decryption, signing and verifying signatures, and key
+generation according to PKCS#1 version 1.5. It can be used as a Python
+library as well as on the commandline. The code was mostly written by
+Sybren A. Stüvel.
+
+Documentation can be found at the [Python-RSA homepage](https://stuvel.eu/rsa).
+
+Download and install using:
+
+ pip install rsa
+
+or download it from the [Python Package Index](https://pypi.org/project/rsa/).
+
+The source code is maintained at [GitHub](https://github.com/sybrenstuvel/python-rsa/) and is
+licensed under the [Apache License, version 2.0](https://www.apache.org/licenses/LICENSE-2.0)
+
+Changes in 4.1-4.4
+------------------
+
+Version 4.1 dropped support for Python 2.7, and soon after that version 4.2 was released. Neither of the two made it explicit in `setup.cfg` that Python 3.5 or newer is required. This caused issues on Python 2.7, as Pip happily upgraded to the new version.
+
+Version 4.3 is a re-tagged release of version 4.0. It is the last to support Python 2.7.
+
+Version 4.4 will be a re-tagged release of version 4.2, and explicitly require Python 3.5 or newer.
+
+
+Major changes in 4.0
+--------------------
+
+Version 3.4 was the last version in the 3.x range. Version 4.0 drops the following modules,
+as they are insecure:
+
+- `rsa._version133`
+- `rsa._version200`
+- `rsa.bigfile`
+- `rsa.varblock`
+
+Those modules were marked as deprecated in version 3.4.
+
+Furthermore, in 4.0 the I/O functions is streamlined to always work with bytes on all
+supported versions of Python.
+
+Version 4.0 drops support for Python 2.6 and 3.3.
+
+
diff --git a/contrib/python/rsa/py2/.dist-info/entry_points.txt b/contrib/python/rsa/py2/.dist-info/entry_points.txt
new file mode 100644
index 0000000000..1c27571272
--- /dev/null
+++ b/contrib/python/rsa/py2/.dist-info/entry_points.txt
@@ -0,0 +1,8 @@
+[console_scripts]
+pyrsa-decrypt = rsa.cli:decrypt
+pyrsa-encrypt = rsa.cli:encrypt
+pyrsa-keygen = rsa.cli:keygen
+pyrsa-priv2pub = rsa.util:private_to_public
+pyrsa-sign = rsa.cli:sign
+pyrsa-verify = rsa.cli:verify
+
diff --git a/contrib/python/rsa/py2/.dist-info/top_level.txt b/contrib/python/rsa/py2/.dist-info/top_level.txt
new file mode 100644
index 0000000000..703f551006
--- /dev/null
+++ b/contrib/python/rsa/py2/.dist-info/top_level.txt
@@ -0,0 +1 @@
+rsa
diff --git a/contrib/python/rsa/py2/LICENSE b/contrib/python/rsa/py2/LICENSE
new file mode 100644
index 0000000000..67589cbb86
--- /dev/null
+++ b/contrib/python/rsa/py2/LICENSE
@@ -0,0 +1,13 @@
+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.
diff --git a/contrib/python/rsa/py2/README.md b/contrib/python/rsa/py2/README.md
new file mode 100644
index 0000000000..b7df4fbde4
--- /dev/null
+++ b/contrib/python/rsa/py2/README.md
@@ -0,0 +1,52 @@
+Pure Python RSA implementation
+==============================
+
+[![PyPI](https://img.shields.io/pypi/v/rsa.svg)](https://pypi.org/project/rsa/)
+[![Build Status](https://travis-ci.org/sybrenstuvel/python-rsa.svg?branch=master)](https://travis-ci.org/sybrenstuvel/python-rsa)
+[![Coverage Status](https://coveralls.io/repos/github/sybrenstuvel/python-rsa/badge.svg?branch=master)](https://coveralls.io/github/sybrenstuvel/python-rsa?branch=master)
+[![Code Climate](https://img.shields.io/codeclimate/github/sybrenstuvel/python-rsa.svg)](https://codeclimate.com/github/sybrenstuvel/python-rsa)
+
+[Python-RSA](https://stuvel.eu/rsa) is a pure-Python RSA implementation. It supports
+encryption and decryption, signing and verifying signatures, and key
+generation according to PKCS#1 version 1.5. It can be used as a Python
+library as well as on the commandline. The code was mostly written by
+Sybren A. Stüvel.
+
+Documentation can be found at the [Python-RSA homepage](https://stuvel.eu/rsa).
+
+Download and install using:
+
+ pip install rsa
+
+or download it from the [Python Package Index](https://pypi.org/project/rsa/).
+
+The source code is maintained at [GitHub](https://github.com/sybrenstuvel/python-rsa/) and is
+licensed under the [Apache License, version 2.0](https://www.apache.org/licenses/LICENSE-2.0)
+
+Changes in 4.1-4.4
+------------------
+
+Version 4.1 dropped support for Python 2.7, and soon after that version 4.2 was released. Neither of the two made it explicit in `setup.cfg` that Python 3.5 or newer is required. This caused issues on Python 2.7, as Pip happily upgraded to the new version.
+
+Version 4.3 is a re-tagged release of version 4.0. It is the last to support Python 2.7.
+
+Version 4.4 will be a re-tagged release of version 4.2, and explicitly require Python 3.5 or newer.
+
+
+Major changes in 4.0
+--------------------
+
+Version 3.4 was the last version in the 3.x range. Version 4.0 drops the following modules,
+as they are insecure:
+
+- `rsa._version133`
+- `rsa._version200`
+- `rsa.bigfile`
+- `rsa.varblock`
+
+Those modules were marked as deprecated in version 3.4.
+
+Furthermore, in 4.0 the I/O functions is streamlined to always work with bytes on all
+supported versions of Python.
+
+Version 4.0 drops support for Python 2.6 and 3.3.
diff --git a/contrib/python/rsa/py2/rsa/__init__.py b/contrib/python/rsa/py2/rsa/__init__.py
new file mode 100644
index 0000000000..af5487f3d3
--- /dev/null
+++ b/contrib/python/rsa/py2/rsa/__init__.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.
+"""RSA module
+
+Module for calculating large primes, and RSA encryption, decryption, signing
+and verification. Includes generating public and private keys.
+
+WARNING: this implementation does not use compression of the cleartext input to
+prevent repetitions, or other common security improvements. Use with care.
+
+"""
+
+from rsa.key import newkeys, PrivateKey, PublicKey
+from rsa.pkcs1 import encrypt, decrypt, sign, verify, DecryptionError, \
+ VerificationError, find_signature_hash, sign_hash, compute_hash
+
+__author__ = "Sybren Stuvel, Barry Mead and Yesudeep Mangalapilly"
+__date__ = "2020-06-12"
+__version__ = '4.5'
+
+# Do doctest if we're run directly
+if __name__ == "__main__":
+ import doctest
+
+ doctest.testmod()
+
+__all__ = ["newkeys", "encrypt", "decrypt", "sign", "verify", 'PublicKey',
+ 'PrivateKey', 'DecryptionError', 'VerificationError',
+ 'compute_hash', 'sign_hash']
diff --git a/contrib/python/rsa/py2/rsa/_compat.py b/contrib/python/rsa/py2/rsa/_compat.py
new file mode 100644
index 0000000000..71197a55b8
--- /dev/null
+++ b/contrib/python/rsa/py2/rsa/_compat.py
@@ -0,0 +1,162 @@
+# -*- 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.
+
+"""Python compatibility wrappers."""
+
+from __future__ import absolute_import
+
+import itertools
+import sys
+from struct import pack
+
+MAX_INT = sys.maxsize
+MAX_INT64 = (1 << 63) - 1
+MAX_INT32 = (1 << 31) - 1
+MAX_INT16 = (1 << 15) - 1
+
+PY2 = sys.version_info[0] == 2
+
+# Determine the word size of the processor.
+if MAX_INT == MAX_INT64:
+ # 64-bit processor.
+ MACHINE_WORD_SIZE = 64
+elif MAX_INT == MAX_INT32:
+ # 32-bit processor.
+ MACHINE_WORD_SIZE = 32
+else:
+ # Else we just assume 64-bit processor keeping up with modern times.
+ MACHINE_WORD_SIZE = 64
+
+if PY2:
+ integer_types = (int, long)
+ range = xrange
+ zip = itertools.izip
+else:
+ integer_types = (int, )
+ range = range
+ zip = zip
+
+
+def write_to_stdout(data):
+ """Writes bytes to stdout
+
+ :type data: bytes
+ """
+ if PY2:
+ sys.stdout.write(data)
+ else:
+ # On Py3 we must use the buffer interface to write bytes.
+ sys.stdout.buffer.write(data)
+
+
+def is_bytes(obj):
+ """
+ Determines whether the given value is a byte string.
+
+ :param obj:
+ The value to test.
+ :returns:
+ ``True`` if ``value`` is a byte string; ``False`` otherwise.
+ """
+ return isinstance(obj, bytes)
+
+
+def is_integer(obj):
+ """
+ Determines whether the given value is an integer.
+
+ :param obj:
+ The value to test.
+ :returns:
+ ``True`` if ``value`` is an integer; ``False`` otherwise.
+ """
+ return isinstance(obj, integer_types)
+
+
+def byte(num):
+ """
+ Converts a number between 0 and 255 (both inclusive) to a base-256 (byte)
+ representation.
+
+ Use it as a replacement for ``chr`` where you are expecting a byte
+ because this will work on all current versions of Python::
+
+ :param num:
+ An unsigned integer between 0 and 255 (both inclusive).
+ :returns:
+ A single byte.
+ """
+ return pack("B", num)
+
+
+def xor_bytes(b1, b2):
+ """
+ Returns the bitwise XOR result between two bytes objects, b1 ^ b2.
+
+ Bitwise XOR operation is commutative, so order of parameters doesn't
+ generate different results. If parameters have different length, extra
+ length of the largest one is ignored.
+
+ :param b1:
+ First bytes object.
+ :param b2:
+ Second bytes object.
+ :returns:
+ Bytes object, result of XOR operation.
+ """
+ if PY2:
+ return ''.join(byte(ord(x) ^ ord(y)) for x, y in zip(b1, b2))
+
+ return bytes(x ^ y for x, y in zip(b1, b2))
+
+
+def get_word_alignment(num, force_arch=64,
+ _machine_word_size=MACHINE_WORD_SIZE):
+ """
+ Returns alignment details for the given number based on the platform
+ Python is running on.
+
+ :param num:
+ Unsigned integral number.
+ :param force_arch:
+ If you don't want to use 64-bit unsigned chunks, set this to
+ anything other than 64. 32-bit chunks will be preferred then.
+ Default 64 will be used when on a 64-bit machine.
+ :param _machine_word_size:
+ (Internal) The machine word size used for alignment.
+ :returns:
+ 4-tuple::
+
+ (word_bits, word_bytes,
+ max_uint, packing_format_type)
+ """
+ max_uint64 = 0xffffffffffffffff
+ max_uint32 = 0xffffffff
+ max_uint16 = 0xffff
+ max_uint8 = 0xff
+
+ if force_arch == 64 and _machine_word_size >= 64 and num > max_uint32:
+ # 64-bit unsigned integer.
+ return 64, 8, max_uint64, "Q"
+ elif num > max_uint16:
+ # 32-bit unsigned integer
+ return 32, 4, max_uint32, "L"
+ elif num > max_uint8:
+ # 16-bit unsigned integer.
+ return 16, 2, max_uint16, "H"
+ else:
+ # 8-bit unsigned integer.
+ return 8, 1, max_uint8, "B"
diff --git a/contrib/python/rsa/py2/rsa/asn1.py b/contrib/python/rsa/py2/rsa/asn1.py
new file mode 100644
index 0000000000..b724b8f53d
--- /dev/null
+++ b/contrib/python/rsa/py2/rsa/asn1.py
@@ -0,0 +1,53 @@
+# -*- 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.
+
+"""ASN.1 definitions.
+
+Not all ASN.1-handling code use these definitions, but when it does, they should be here.
+"""
+
+from pyasn1.type import univ, namedtype, tag
+
+
+class PubKeyHeader(univ.Sequence):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('oid', univ.ObjectIdentifier()),
+ namedtype.NamedType('parameters', univ.Null()),
+ )
+
+
+class OpenSSLPubKey(univ.Sequence):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('header', PubKeyHeader()),
+
+ # This little hack (the implicit tag) allows us to get a Bit String as Octet String
+ namedtype.NamedType('key', univ.OctetString().subtype(
+ implicitTag=tag.Tag(tagClass=0, tagFormat=0, tagId=3))),
+ )
+
+
+class AsnPubKey(univ.Sequence):
+ """ASN.1 contents of DER encoded public key:
+
+ RSAPublicKey ::= SEQUENCE {
+ modulus INTEGER, -- n
+ publicExponent INTEGER, -- e
+ """
+
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('modulus', univ.Integer()),
+ namedtype.NamedType('publicExponent', univ.Integer()),
+ )
diff --git a/contrib/python/rsa/py2/rsa/cli.py b/contrib/python/rsa/py2/rsa/cli.py
new file mode 100644
index 0000000000..6450af427f
--- /dev/null
+++ b/contrib/python/rsa/py2/rsa/cli.py
@@ -0,0 +1,288 @@
+# -*- 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.
+
+"""Commandline scripts.
+
+These scripts are called by the executables defined in setup.py.
+"""
+
+from __future__ import with_statement, print_function
+
+import abc
+import sys
+from optparse import OptionParser
+
+import rsa
+import rsa.pkcs1
+
+HASH_METHODS = sorted(rsa.pkcs1.HASH_METHODS.keys())
+
+
+def keygen():
+ """Key generator."""
+
+ # Parse the CLI options
+ parser = OptionParser(usage='usage: %prog [options] keysize',
+ description='Generates a new RSA keypair of "keysize" bits.')
+
+ parser.add_option('--pubout', type='string',
+ help='Output filename for the public key. The public key is '
+ 'not saved if this option is not present. You can use '
+ 'pyrsa-priv2pub to create the public key file later.')
+
+ parser.add_option('-o', '--out', type='string',
+ help='Output filename for the private key. The key is '
+ 'written to stdout if this option is not present.')
+
+ parser.add_option('--form',
+ help='key format of the private and public keys - default PEM',
+ choices=('PEM', 'DER'), default='PEM')
+
+ (cli, cli_args) = parser.parse_args(sys.argv[1:])
+
+ if len(cli_args) != 1:
+ parser.print_help()
+ raise SystemExit(1)
+
+ try:
+ keysize = int(cli_args[0])
+ except ValueError:
+ parser.print_help()
+ print('Not a valid number: %s' % cli_args[0], file=sys.stderr)
+ raise SystemExit(1)
+
+ print('Generating %i-bit key' % keysize, file=sys.stderr)
+ (pub_key, priv_key) = rsa.newkeys(keysize)
+
+ # Save public key
+ if cli.pubout:
+ print('Writing public key to %s' % cli.pubout, file=sys.stderr)
+ data = pub_key.save_pkcs1(format=cli.form)
+ with open(cli.pubout, 'wb') as outfile:
+ outfile.write(data)
+
+ # Save private key
+ data = priv_key.save_pkcs1(format=cli.form)
+
+ if cli.out:
+ print('Writing private key to %s' % cli.out, file=sys.stderr)
+ with open(cli.out, 'wb') as outfile:
+ outfile.write(data)
+ else:
+ print('Writing private key to stdout', file=sys.stderr)
+ rsa._compat.write_to_stdout(data)
+
+
+class CryptoOperation(object):
+ """CLI callable that operates with input, output, and a key."""
+
+ __metaclass__ = abc.ABCMeta
+
+ keyname = 'public' # or 'private'
+ usage = 'usage: %%prog [options] %(keyname)s_key'
+ description = None
+ operation = 'decrypt'
+ operation_past = 'decrypted'
+ operation_progressive = 'decrypting'
+ input_help = 'Name of the file to %(operation)s. Reads from stdin if ' \
+ 'not specified.'
+ output_help = 'Name of the file to write the %(operation_past)s file ' \
+ 'to. Written to stdout if this option is not present.'
+ expected_cli_args = 1
+ has_output = True
+
+ key_class = rsa.PublicKey
+
+ def __init__(self):
+ self.usage = self.usage % self.__class__.__dict__
+ self.input_help = self.input_help % self.__class__.__dict__
+ self.output_help = self.output_help % self.__class__.__dict__
+
+ @abc.abstractmethod
+ def perform_operation(self, indata, key, cli_args):
+ """Performs the program's operation.
+
+ Implement in a subclass.
+
+ :returns: the data to write to the output.
+ """
+
+ def __call__(self):
+ """Runs the program."""
+
+ (cli, cli_args) = self.parse_cli()
+
+ key = self.read_key(cli_args[0], cli.keyform)
+
+ indata = self.read_infile(cli.input)
+
+ print(self.operation_progressive.title(), file=sys.stderr)
+ outdata = self.perform_operation(indata, key, cli_args)
+
+ if self.has_output:
+ self.write_outfile(outdata, cli.output)
+
+ def parse_cli(self):
+ """Parse the CLI options
+
+ :returns: (cli_opts, cli_args)
+ """
+
+ parser = OptionParser(usage=self.usage, description=self.description)
+
+ parser.add_option('-i', '--input', type='string', help=self.input_help)
+
+ if self.has_output:
+ parser.add_option('-o', '--output', type='string', help=self.output_help)
+
+ parser.add_option('--keyform',
+ help='Key format of the %s key - default PEM' % self.keyname,
+ choices=('PEM', 'DER'), default='PEM')
+
+ (cli, cli_args) = parser.parse_args(sys.argv[1:])
+
+ if len(cli_args) != self.expected_cli_args:
+ parser.print_help()
+ raise SystemExit(1)
+
+ return cli, cli_args
+
+ def read_key(self, filename, keyform):
+ """Reads a public or private key."""
+
+ print('Reading %s key from %s' % (self.keyname, filename), file=sys.stderr)
+ with open(filename, 'rb') as keyfile:
+ keydata = keyfile.read()
+
+ return self.key_class.load_pkcs1(keydata, keyform)
+
+ def read_infile(self, inname):
+ """Read the input file"""
+
+ if inname:
+ print('Reading input from %s' % inname, file=sys.stderr)
+ with open(inname, 'rb') as infile:
+ return infile.read()
+
+ print('Reading input from stdin', file=sys.stderr)
+ return sys.stdin.read()
+
+ def write_outfile(self, outdata, outname):
+ """Write the output file"""
+
+ if outname:
+ print('Writing output to %s' % outname, file=sys.stderr)
+ with open(outname, 'wb') as outfile:
+ outfile.write(outdata)
+ else:
+ print('Writing output to stdout', file=sys.stderr)
+ rsa._compat.write_to_stdout(outdata)
+
+
+class EncryptOperation(CryptoOperation):
+ """Encrypts a file."""
+
+ keyname = 'public'
+ description = ('Encrypts a file. The file must be shorter than the key '
+ 'length in order to be encrypted.')
+ operation = 'encrypt'
+ operation_past = 'encrypted'
+ operation_progressive = 'encrypting'
+
+ def perform_operation(self, indata, pub_key, cli_args=None):
+ """Encrypts files."""
+
+ return rsa.encrypt(indata, pub_key)
+
+
+class DecryptOperation(CryptoOperation):
+ """Decrypts a file."""
+
+ keyname = 'private'
+ description = ('Decrypts a file. The original file must be shorter than '
+ 'the key length in order to have been encrypted.')
+ operation = 'decrypt'
+ operation_past = 'decrypted'
+ operation_progressive = 'decrypting'
+ key_class = rsa.PrivateKey
+
+ def perform_operation(self, indata, priv_key, cli_args=None):
+ """Decrypts files."""
+
+ return rsa.decrypt(indata, priv_key)
+
+
+class SignOperation(CryptoOperation):
+ """Signs a file."""
+
+ keyname = 'private'
+ usage = 'usage: %%prog [options] private_key hash_method'
+ description = ('Signs a file, outputs the signature. Choose the hash '
+ 'method from %s' % ', '.join(HASH_METHODS))
+ operation = 'sign'
+ operation_past = 'signature'
+ operation_progressive = 'Signing'
+ key_class = rsa.PrivateKey
+ expected_cli_args = 2
+
+ output_help = ('Name of the file to write the signature to. Written '
+ 'to stdout if this option is not present.')
+
+ def perform_operation(self, indata, priv_key, cli_args):
+ """Signs files."""
+
+ hash_method = cli_args[1]
+ if hash_method not in HASH_METHODS:
+ raise SystemExit('Invalid hash method, choose one of %s' %
+ ', '.join(HASH_METHODS))
+
+ return rsa.sign(indata, priv_key, hash_method)
+
+
+class VerifyOperation(CryptoOperation):
+ """Verify a signature."""
+
+ keyname = 'public'
+ usage = 'usage: %%prog [options] public_key signature_file'
+ description = ('Verifies a signature, exits with status 0 upon success, '
+ 'prints an error message and exits with status 1 upon error.')
+ operation = 'verify'
+ operation_past = 'verified'
+ operation_progressive = 'Verifying'
+ key_class = rsa.PublicKey
+ expected_cli_args = 2
+ has_output = False
+
+ def perform_operation(self, indata, pub_key, cli_args):
+ """Verifies files."""
+
+ signature_file = cli_args[1]
+
+ with open(signature_file, 'rb') as sigfile:
+ signature = sigfile.read()
+
+ try:
+ rsa.verify(indata, signature, pub_key)
+ except rsa.VerificationError:
+ raise SystemExit('Verification failed.')
+
+ print('Verification OK', file=sys.stderr)
+
+
+encrypt = EncryptOperation()
+decrypt = DecryptOperation()
+sign = SignOperation()
+verify = VerifyOperation()
diff --git a/contrib/python/rsa/py2/rsa/common.py b/contrib/python/rsa/py2/rsa/common.py
new file mode 100644
index 0000000000..f7aa2d1496
--- /dev/null
+++ b/contrib/python/rsa/py2/rsa/common.py
@@ -0,0 +1,188 @@
+# -*- 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.
+
+from rsa._compat import zip
+
+"""Common functionality shared by several modules."""
+
+
+class NotRelativePrimeError(ValueError):
+ def __init__(self, a, b, d, msg=None):
+ super(NotRelativePrimeError, self).__init__(
+ msg or "%d and %d are not relatively prime, divider=%i" % (a, b, d))
+ self.a = a
+ self.b = b
+ self.d = d
+
+
+def bit_size(num):
+ """
+ Number of bits needed to represent a integer excluding any prefix
+ 0 bits.
+
+ Usage::
+
+ >>> bit_size(1023)
+ 10
+ >>> bit_size(1024)
+ 11
+ >>> bit_size(1025)
+ 11
+
+ :param num:
+ Integer value. If num is 0, returns 0. Only the absolute value of the
+ number is considered. Therefore, signed integers will be abs(num)
+ before the number's bit length is determined.
+ :returns:
+ Returns the number of bits in the integer.
+ """
+
+ try:
+ return num.bit_length()
+ except AttributeError:
+ raise TypeError('bit_size(num) only supports integers, not %r' % type(num))
+
+
+def byte_size(number):
+ """
+ Returns the number of bytes required to hold a specific long number.
+
+ The number of bytes is rounded up.
+
+ Usage::
+
+ >>> byte_size(1 << 1023)
+ 128
+ >>> byte_size((1 << 1024) - 1)
+ 128
+ >>> byte_size(1 << 1024)
+ 129
+
+ :param number:
+ An unsigned integer
+ :returns:
+ The number of bytes required to hold a specific long number.
+ """
+ if number == 0:
+ return 1
+ return ceil_div(bit_size(number), 8)
+
+
+def ceil_div(num, div):
+ """
+ Returns the ceiling function of a division between `num` and `div`.
+
+ Usage::
+
+ >>> ceil_div(100, 7)
+ 15
+ >>> ceil_div(100, 10)
+ 10
+ >>> ceil_div(1, 4)
+ 1
+
+ :param num: Division's numerator, a number
+ :param div: Division's divisor, a number
+
+ :return: Rounded up result of the division between the parameters.
+ """
+ quanta, mod = divmod(num, div)
+ if mod:
+ quanta += 1
+ return quanta
+
+
+def extended_gcd(a, b):
+ """Returns a tuple (r, i, j) such that r = gcd(a, b) = ia + jb
+ """
+ # r = gcd(a,b) i = multiplicitive inverse of a mod b
+ # or j = multiplicitive inverse of b mod a
+ # Neg return values for i or j are made positive mod b or a respectively
+ # Iterateive Version is faster and uses much less stack space
+ x = 0
+ y = 1
+ lx = 1
+ ly = 0
+ oa = a # Remember original a/b to remove
+ ob = b # negative values from return results
+ while b != 0:
+ q = a // b
+ (a, b) = (b, a % b)
+ (x, lx) = ((lx - (q * x)), x)
+ (y, ly) = ((ly - (q * y)), y)
+ if lx < 0:
+ lx += ob # If neg wrap modulo orignal b
+ if ly < 0:
+ ly += oa # If neg wrap modulo orignal a
+ return a, lx, ly # Return only positive values
+
+
+def inverse(x, n):
+ """Returns the inverse of x % n under multiplication, a.k.a x^-1 (mod n)
+
+ >>> inverse(7, 4)
+ 3
+ >>> (inverse(143, 4) * 143) % 4
+ 1
+ """
+
+ (divider, inv, _) = extended_gcd(x, n)
+
+ if divider != 1:
+ raise NotRelativePrimeError(x, n, divider)
+
+ return inv
+
+
+def crt(a_values, modulo_values):
+ """Chinese Remainder Theorem.
+
+ Calculates x such that x = a[i] (mod m[i]) for each i.
+
+ :param a_values: the a-values of the above equation
+ :param modulo_values: the m-values of the above equation
+ :returns: x such that x = a[i] (mod m[i]) for each i
+
+
+ >>> crt([2, 3], [3, 5])
+ 8
+
+ >>> crt([2, 3, 2], [3, 5, 7])
+ 23
+
+ >>> crt([2, 3, 0], [7, 11, 15])
+ 135
+ """
+
+ m = 1
+ x = 0
+
+ for modulo in modulo_values:
+ m *= modulo
+
+ for (m_i, a_i) in zip(modulo_values, a_values):
+ M_i = m // m_i
+ inv = inverse(M_i, m_i)
+
+ x = (x + a_i * M_i * inv) % m
+
+ return x
+
+
+if __name__ == '__main__':
+ import doctest
+
+ doctest.testmod()
diff --git a/contrib/python/rsa/py2/rsa/core.py b/contrib/python/rsa/py2/rsa/core.py
new file mode 100644
index 0000000000..b3114d9e9c
--- /dev/null
+++ b/contrib/python/rsa/py2/rsa/core.py
@@ -0,0 +1,57 @@
+# -*- 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.
+
+"""Core mathematical operations.
+
+This is the actual core RSA implementation, which is only defined
+mathematically on integers.
+"""
+
+from rsa._compat import is_integer
+
+
+def assert_int(var, name):
+ if is_integer(var):
+ return
+
+ raise TypeError('%s should be an integer, not %s' % (name, var.__class__))
+
+
+def encrypt_int(message, ekey, n):
+ """Encrypts a message using encryption key 'ekey', working modulo n"""
+
+ assert_int(message, 'message')
+ assert_int(ekey, 'ekey')
+ assert_int(n, 'n')
+
+ if message < 0:
+ raise ValueError('Only non-negative numbers are supported')
+
+ if message > n:
+ raise OverflowError("The message %i is too long for n=%i" % (message, n))
+
+ return pow(message, ekey, n)
+
+
+def decrypt_int(cyphertext, dkey, n):
+ """Decrypts a cypher text using the decryption key 'dkey', working modulo n"""
+
+ assert_int(cyphertext, 'cyphertext')
+ assert_int(dkey, 'dkey')
+ assert_int(n, 'n')
+
+ message = pow(cyphertext, dkey, n)
+ return message
diff --git a/contrib/python/rsa/py2/rsa/key.py b/contrib/python/rsa/py2/rsa/key.py
new file mode 100644
index 0000000000..1e2f6fe455
--- /dev/null
+++ b/contrib/python/rsa/py2/rsa/key.py
@@ -0,0 +1,798 @@
+# -*- 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.
+
+"""RSA key generation code.
+
+Create new keys with the newkeys() function. It will give you a PublicKey and a
+PrivateKey object.
+
+Loading and saving keys requires the pyasn1 module. This module is imported as
+late as possible, such that other functionality will remain working in absence
+of pyasn1.
+
+.. note::
+
+ Storing public and private keys via the `pickle` module is possible.
+ However, it is insecure to load a key from an untrusted source.
+ The pickle module is not secure against erroneous or maliciously
+ constructed data. Never unpickle data received from an untrusted
+ or unauthenticated source.
+
+"""
+
+import logging
+import warnings
+
+from rsa._compat import range
+import rsa.prime
+import rsa.pem
+import rsa.common
+import rsa.randnum
+import rsa.core
+
+
+log = logging.getLogger(__name__)
+DEFAULT_EXPONENT = 65537
+
+
+class AbstractKey(object):
+ """Abstract superclass for private and public keys."""
+
+ __slots__ = ('n', 'e')
+
+ def __init__(self, n, e):
+ self.n = n
+ self.e = e
+
+ @classmethod
+ def _load_pkcs1_pem(cls, keyfile):
+ """Loads a key in PKCS#1 PEM format, implement in a subclass.
+
+ :param keyfile: contents of a PEM-encoded file that contains
+ the public key.
+ :type keyfile: bytes
+
+ :return: the loaded key
+ :rtype: AbstractKey
+ """
+
+ @classmethod
+ def _load_pkcs1_der(cls, keyfile):
+ """Loads a key in PKCS#1 PEM format, implement in a subclass.
+
+ :param keyfile: contents of a DER-encoded file that contains
+ the public key.
+ :type keyfile: bytes
+
+ :return: the loaded key
+ :rtype: AbstractKey
+ """
+
+ def _save_pkcs1_pem(self):
+ """Saves the key in PKCS#1 PEM format, implement in a subclass.
+
+ :returns: the PEM-encoded key.
+ :rtype: bytes
+ """
+
+ def _save_pkcs1_der(self):
+ """Saves the key in PKCS#1 DER format, implement in a subclass.
+
+ :returns: the DER-encoded key.
+ :rtype: bytes
+ """
+
+ @classmethod
+ def load_pkcs1(cls, keyfile, format='PEM'):
+ """Loads a key in PKCS#1 DER or PEM format.
+
+ :param keyfile: contents of a DER- or PEM-encoded file that contains
+ the key.
+ :type keyfile: bytes
+ :param format: the format of the file to load; 'PEM' or 'DER'
+ :type format: str
+
+ :return: the loaded key
+ :rtype: AbstractKey
+ """
+
+ methods = {
+ 'PEM': cls._load_pkcs1_pem,
+ 'DER': cls._load_pkcs1_der,
+ }
+
+ method = cls._assert_format_exists(format, methods)
+ return method(keyfile)
+
+ @staticmethod
+ def _assert_format_exists(file_format, methods):
+ """Checks whether the given file format exists in 'methods'.
+ """
+
+ try:
+ return methods[file_format]
+ except KeyError:
+ formats = ', '.join(sorted(methods.keys()))
+ raise ValueError('Unsupported format: %r, try one of %s' % (file_format,
+ formats))
+
+ def save_pkcs1(self, format='PEM'):
+ """Saves the key in PKCS#1 DER or PEM format.
+
+ :param format: the format to save; 'PEM' or 'DER'
+ :type format: str
+ :returns: the DER- or PEM-encoded key.
+ :rtype: bytes
+ """
+
+ methods = {
+ 'PEM': self._save_pkcs1_pem,
+ 'DER': self._save_pkcs1_der,
+ }
+
+ method = self._assert_format_exists(format, methods)
+ return method()
+
+ def blind(self, message, r):
+ """Performs blinding on the message using random number 'r'.
+
+ :param message: the message, as integer, to blind.
+ :type message: int
+ :param r: the random number to blind with.
+ :type r: int
+ :return: the blinded message.
+ :rtype: int
+
+ The blinding is such that message = unblind(decrypt(blind(encrypt(message))).
+
+ See https://en.wikipedia.org/wiki/Blinding_%28cryptography%29
+ """
+
+ return (message * pow(r, self.e, self.n)) % self.n
+
+ def unblind(self, blinded, r):
+ """Performs blinding on the message using random number 'r'.
+
+ :param blinded: the blinded message, as integer, to unblind.
+ :param r: the random number to unblind with.
+ :return: the original message.
+
+ The blinding is such that message = unblind(decrypt(blind(encrypt(message))).
+
+ See https://en.wikipedia.org/wiki/Blinding_%28cryptography%29
+ """
+
+ return (rsa.common.inverse(r, self.n) * blinded) % self.n
+
+
+class PublicKey(AbstractKey):
+ """Represents a public RSA key.
+
+ This key is also known as the 'encryption key'. It contains the 'n' and 'e'
+ values.
+
+ Supports attributes as well as dictionary-like access. Attribute access is
+ faster, though.
+
+ >>> PublicKey(5, 3)
+ PublicKey(5, 3)
+
+ >>> key = PublicKey(5, 3)
+ >>> key.n
+ 5
+ >>> key['n']
+ 5
+ >>> key.e
+ 3
+ >>> key['e']
+ 3
+
+ """
+
+ __slots__ = ('n', 'e')
+
+ def __getitem__(self, key):
+ return getattr(self, key)
+
+ def __repr__(self):
+ return 'PublicKey(%i, %i)' % (self.n, self.e)
+
+ def __getstate__(self):
+ """Returns the key as tuple for pickling."""
+ return self.n, self.e
+
+ def __setstate__(self, state):
+ """Sets the key from tuple."""
+ self.n, self.e = state
+
+ def __eq__(self, other):
+ if other is None:
+ return False
+
+ if not isinstance(other, PublicKey):
+ return False
+
+ return self.n == other.n and self.e == other.e
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ def __hash__(self):
+ return hash((self.n, self.e))
+
+ @classmethod
+ def _load_pkcs1_der(cls, keyfile):
+ """Loads a key in PKCS#1 DER format.
+
+ :param keyfile: contents of a DER-encoded file that contains the public
+ key.
+ :return: a PublicKey object
+
+ First let's construct a DER encoded key:
+
+ >>> import base64
+ >>> b64der = 'MAwCBQCNGmYtAgMBAAE='
+ >>> der = base64.standard_b64decode(b64der)
+
+ This loads the file:
+
+ >>> PublicKey._load_pkcs1_der(der)
+ PublicKey(2367317549, 65537)
+
+ """
+
+ from pyasn1.codec.der import decoder
+ from rsa.asn1 import AsnPubKey
+
+ (priv, _) = decoder.decode(keyfile, asn1Spec=AsnPubKey())
+ return cls(n=int(priv['modulus']), e=int(priv['publicExponent']))
+
+ def _save_pkcs1_der(self):
+ """Saves the public key in PKCS#1 DER format.
+
+ :returns: the DER-encoded public key.
+ :rtype: bytes
+ """
+
+ from pyasn1.codec.der import encoder
+ from rsa.asn1 import AsnPubKey
+
+ # Create the ASN object
+ asn_key = AsnPubKey()
+ asn_key.setComponentByName('modulus', self.n)
+ asn_key.setComponentByName('publicExponent', self.e)
+
+ return encoder.encode(asn_key)
+
+ @classmethod
+ def _load_pkcs1_pem(cls, keyfile):
+ """Loads a PKCS#1 PEM-encoded public key file.
+
+ The contents of the file before the "-----BEGIN RSA PUBLIC KEY-----" and
+ after the "-----END RSA PUBLIC KEY-----" lines is ignored.
+
+ :param keyfile: contents of a PEM-encoded file that contains the public
+ key.
+ :return: a PublicKey object
+ """
+
+ der = rsa.pem.load_pem(keyfile, 'RSA PUBLIC KEY')
+ return cls._load_pkcs1_der(der)
+
+ def _save_pkcs1_pem(self):
+ """Saves a PKCS#1 PEM-encoded public key file.
+
+ :return: contents of a PEM-encoded file that contains the public key.
+ :rtype: bytes
+ """
+
+ der = self._save_pkcs1_der()
+ return rsa.pem.save_pem(der, 'RSA PUBLIC KEY')
+
+ @classmethod
+ def load_pkcs1_openssl_pem(cls, keyfile):
+ """Loads a PKCS#1.5 PEM-encoded public key file from OpenSSL.
+
+ These files can be recognised in that they start with BEGIN PUBLIC KEY
+ rather than BEGIN RSA PUBLIC KEY.
+
+ The contents of the file before the "-----BEGIN PUBLIC KEY-----" and
+ after the "-----END PUBLIC KEY-----" lines is ignored.
+
+ :param keyfile: contents of a PEM-encoded file that contains the public
+ key, from OpenSSL.
+ :type keyfile: bytes
+ :return: a PublicKey object
+ """
+
+ der = rsa.pem.load_pem(keyfile, 'PUBLIC KEY')
+ return cls.load_pkcs1_openssl_der(der)
+
+ @classmethod
+ def load_pkcs1_openssl_der(cls, keyfile):
+ """Loads a PKCS#1 DER-encoded public key file from OpenSSL.
+
+ :param keyfile: contents of a DER-encoded file that contains the public
+ key, from OpenSSL.
+ :return: a PublicKey object
+ :rtype: bytes
+
+ """
+
+ from rsa.asn1 import OpenSSLPubKey
+ from pyasn1.codec.der import decoder
+ from pyasn1.type import univ
+
+ (keyinfo, _) = decoder.decode(keyfile, asn1Spec=OpenSSLPubKey())
+
+ if keyinfo['header']['oid'] != univ.ObjectIdentifier('1.2.840.113549.1.1.1'):
+ raise TypeError("This is not a DER-encoded OpenSSL-compatible public key")
+
+ return cls._load_pkcs1_der(keyinfo['key'][1:])
+
+
+class PrivateKey(AbstractKey):
+ """Represents a private RSA key.
+
+ This key is also known as the 'decryption key'. It contains the 'n', 'e',
+ 'd', 'p', 'q' and other values.
+
+ Supports attributes as well as dictionary-like access. Attribute access is
+ faster, though.
+
+ >>> PrivateKey(3247, 65537, 833, 191, 17)
+ PrivateKey(3247, 65537, 833, 191, 17)
+
+ exp1, exp2 and coef will be calculated:
+
+ >>> pk = PrivateKey(3727264081, 65537, 3349121513, 65063, 57287)
+ >>> pk.exp1
+ 55063
+ >>> pk.exp2
+ 10095
+ >>> pk.coef
+ 50797
+
+ """
+
+ __slots__ = ('n', 'e', 'd', 'p', 'q', 'exp1', 'exp2', 'coef')
+
+ def __init__(self, n, e, d, p, q):
+ AbstractKey.__init__(self, n, e)
+ self.d = d
+ self.p = p
+ self.q = q
+
+ # Calculate exponents and coefficient.
+ self.exp1 = int(d % (p - 1))
+ self.exp2 = int(d % (q - 1))
+ self.coef = rsa.common.inverse(q, p)
+
+ def __getitem__(self, key):
+ return getattr(self, key)
+
+ def __repr__(self):
+ return 'PrivateKey(%(n)i, %(e)i, %(d)i, %(p)i, %(q)i)' % self
+
+ def __getstate__(self):
+ """Returns the key as tuple for pickling."""
+ return self.n, self.e, self.d, self.p, self.q, self.exp1, self.exp2, self.coef
+
+ def __setstate__(self, state):
+ """Sets the key from tuple."""
+ self.n, self.e, self.d, self.p, self.q, self.exp1, self.exp2, self.coef = state
+
+ def __eq__(self, other):
+ if other is None:
+ return False
+
+ if not isinstance(other, PrivateKey):
+ return False
+
+ return (self.n == other.n and
+ self.e == other.e and
+ self.d == other.d and
+ self.p == other.p and
+ self.q == other.q and
+ self.exp1 == other.exp1 and
+ self.exp2 == other.exp2 and
+ self.coef == other.coef)
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ def __hash__(self):
+ return hash((self.n, self.e, self.d, self.p, self.q, self.exp1, self.exp2, self.coef))
+
+ def _get_blinding_factor(self):
+ for _ in range(1000):
+ blind_r = rsa.randnum.randint(self.n - 1)
+ if rsa.prime.are_relatively_prime(self.n, blind_r):
+ return blind_r
+ raise RuntimeError('unable to find blinding factor')
+
+ def blinded_decrypt(self, encrypted):
+ """Decrypts the message using blinding to prevent side-channel attacks.
+
+ :param encrypted: the encrypted message
+ :type encrypted: int
+
+ :returns: the decrypted message
+ :rtype: int
+ """
+
+ blind_r = self._get_blinding_factor()
+ blinded = self.blind(encrypted, blind_r) # blind before decrypting
+ decrypted = rsa.core.decrypt_int(blinded, self.d, self.n)
+
+ return self.unblind(decrypted, blind_r)
+
+ def blinded_encrypt(self, message):
+ """Encrypts the message using blinding to prevent side-channel attacks.
+
+ :param message: the message to encrypt
+ :type message: int
+
+ :returns: the encrypted message
+ :rtype: int
+ """
+
+ blind_r = self._get_blinding_factor()
+ blinded = self.blind(message, blind_r) # blind before encrypting
+ encrypted = rsa.core.encrypt_int(blinded, self.d, self.n)
+ return self.unblind(encrypted, blind_r)
+
+ @classmethod
+ def _load_pkcs1_der(cls, keyfile):
+ """Loads a key in PKCS#1 DER format.
+
+ :param keyfile: contents of a DER-encoded file that contains the private
+ key.
+ :type keyfile: bytes
+ :return: a PrivateKey object
+
+ First let's construct a DER encoded key:
+
+ >>> import base64
+ >>> b64der = 'MC4CAQACBQDeKYlRAgMBAAECBQDHn4npAgMA/icCAwDfxwIDANcXAgInbwIDAMZt'
+ >>> der = base64.standard_b64decode(b64der)
+
+ This loads the file:
+
+ >>> PrivateKey._load_pkcs1_der(der)
+ PrivateKey(3727264081, 65537, 3349121513, 65063, 57287)
+
+ """
+
+ from pyasn1.codec.der import decoder
+ (priv, _) = decoder.decode(keyfile)
+
+ # ASN.1 contents of DER encoded private key:
+ #
+ # RSAPrivateKey ::= SEQUENCE {
+ # version Version,
+ # modulus INTEGER, -- n
+ # publicExponent INTEGER, -- e
+ # privateExponent INTEGER, -- d
+ # prime1 INTEGER, -- p
+ # prime2 INTEGER, -- q
+ # exponent1 INTEGER, -- d mod (p-1)
+ # exponent2 INTEGER, -- d mod (q-1)
+ # coefficient INTEGER, -- (inverse of q) mod p
+ # otherPrimeInfos OtherPrimeInfos OPTIONAL
+ # }
+
+ if priv[0] != 0:
+ raise ValueError('Unable to read this file, version %s != 0' % priv[0])
+
+ as_ints = map(int, priv[1:6])
+ key = cls(*as_ints)
+
+ exp1, exp2, coef = map(int, priv[6:9])
+
+ if (key.exp1, key.exp2, key.coef) != (exp1, exp2, coef):
+ warnings.warn(
+ 'You have provided a malformed keyfile. Either the exponents '
+ 'or the coefficient are incorrect. Using the correct values '
+ 'instead.',
+ UserWarning,
+ )
+
+ return key
+
+ def _save_pkcs1_der(self):
+ """Saves the private key in PKCS#1 DER format.
+
+ :returns: the DER-encoded private key.
+ :rtype: bytes
+ """
+
+ from pyasn1.type import univ, namedtype
+ from pyasn1.codec.der import encoder
+
+ class AsnPrivKey(univ.Sequence):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('version', univ.Integer()),
+ namedtype.NamedType('modulus', univ.Integer()),
+ namedtype.NamedType('publicExponent', univ.Integer()),
+ namedtype.NamedType('privateExponent', univ.Integer()),
+ namedtype.NamedType('prime1', univ.Integer()),
+ namedtype.NamedType('prime2', univ.Integer()),
+ namedtype.NamedType('exponent1', univ.Integer()),
+ namedtype.NamedType('exponent2', univ.Integer()),
+ namedtype.NamedType('coefficient', univ.Integer()),
+ )
+
+ # Create the ASN object
+ asn_key = AsnPrivKey()
+ asn_key.setComponentByName('version', 0)
+ asn_key.setComponentByName('modulus', self.n)
+ asn_key.setComponentByName('publicExponent', self.e)
+ asn_key.setComponentByName('privateExponent', self.d)
+ asn_key.setComponentByName('prime1', self.p)
+ asn_key.setComponentByName('prime2', self.q)
+ asn_key.setComponentByName('exponent1', self.exp1)
+ asn_key.setComponentByName('exponent2', self.exp2)
+ asn_key.setComponentByName('coefficient', self.coef)
+
+ return encoder.encode(asn_key)
+
+ @classmethod
+ def _load_pkcs1_pem(cls, keyfile):
+ """Loads a PKCS#1 PEM-encoded private key file.
+
+ The contents of the file before the "-----BEGIN RSA PRIVATE KEY-----" and
+ after the "-----END RSA PRIVATE KEY-----" lines is ignored.
+
+ :param keyfile: contents of a PEM-encoded file that contains the private
+ key.
+ :type keyfile: bytes
+ :return: a PrivateKey object
+ """
+
+ der = rsa.pem.load_pem(keyfile, b'RSA PRIVATE KEY')
+ return cls._load_pkcs1_der(der)
+
+ def _save_pkcs1_pem(self):
+ """Saves a PKCS#1 PEM-encoded private key file.
+
+ :return: contents of a PEM-encoded file that contains the private key.
+ :rtype: bytes
+ """
+
+ der = self._save_pkcs1_der()
+ return rsa.pem.save_pem(der, b'RSA PRIVATE KEY')
+
+
+def find_p_q(nbits, getprime_func=rsa.prime.getprime, accurate=True):
+ """Returns a tuple of two different primes of nbits bits each.
+
+ The resulting p * q has exacty 2 * nbits bits, and the returned p and q
+ will not be equal.
+
+ :param nbits: the number of bits in each of p and q.
+ :param getprime_func: the getprime function, defaults to
+ :py:func:`rsa.prime.getprime`.
+
+ *Introduced in Python-RSA 3.1*
+
+ :param accurate: whether to enable accurate mode or not.
+ :returns: (p, q), where p > q
+
+ >>> (p, q) = find_p_q(128)
+ >>> from rsa import common
+ >>> common.bit_size(p * q)
+ 256
+
+ When not in accurate mode, the number of bits can be slightly less
+
+ >>> (p, q) = find_p_q(128, accurate=False)
+ >>> from rsa import common
+ >>> common.bit_size(p * q) <= 256
+ True
+ >>> common.bit_size(p * q) > 240
+ True
+
+ """
+
+ total_bits = nbits * 2
+
+ # Make sure that p and q aren't too close or the factoring programs can
+ # factor n.
+ shift = nbits // 16
+ pbits = nbits + shift
+ qbits = nbits - shift
+
+ # Choose the two initial primes
+ log.debug('find_p_q(%i): Finding p', nbits)
+ p = getprime_func(pbits)
+ log.debug('find_p_q(%i): Finding q', nbits)
+ q = getprime_func(qbits)
+
+ def is_acceptable(p, q):
+ """Returns True iff p and q are acceptable:
+
+ - p and q differ
+ - (p * q) has the right nr of bits (when accurate=True)
+ """
+
+ if p == q:
+ return False
+
+ if not accurate:
+ return True
+
+ # Make sure we have just the right amount of bits
+ found_size = rsa.common.bit_size(p * q)
+ return total_bits == found_size
+
+ # Keep choosing other primes until they match our requirements.
+ change_p = False
+ while not is_acceptable(p, q):
+ # Change p on one iteration and q on the other
+ if change_p:
+ p = getprime_func(pbits)
+ else:
+ q = getprime_func(qbits)
+
+ change_p = not change_p
+
+ # We want p > q as described on
+ # http://www.di-mgt.com.au/rsa_alg.html#crt
+ return max(p, q), min(p, q)
+
+
+def calculate_keys_custom_exponent(p, q, exponent):
+ """Calculates an encryption and a decryption key given p, q and an exponent,
+ and returns them as a tuple (e, d)
+
+ :param p: the first large prime
+ :param q: the second large prime
+ :param exponent: the exponent for the key; only change this if you know
+ what you're doing, as the exponent influences how difficult your
+ private key can be cracked. A very common choice for e is 65537.
+ :type exponent: int
+
+ """
+
+ phi_n = (p - 1) * (q - 1)
+
+ try:
+ d = rsa.common.inverse(exponent, phi_n)
+ except rsa.common.NotRelativePrimeError as ex:
+ raise rsa.common.NotRelativePrimeError(
+ exponent, phi_n, ex.d,
+ msg="e (%d) and phi_n (%d) are not relatively prime (divider=%i)" %
+ (exponent, phi_n, ex.d))
+
+ if (exponent * d) % phi_n != 1:
+ raise ValueError("e (%d) and d (%d) are not mult. inv. modulo "
+ "phi_n (%d)" % (exponent, d, phi_n))
+
+ return exponent, d
+
+
+def calculate_keys(p, q):
+ """Calculates an encryption and a decryption key given p and q, and
+ returns them as a tuple (e, d)
+
+ :param p: the first large prime
+ :param q: the second large prime
+
+ :return: tuple (e, d) with the encryption and decryption exponents.
+ """
+
+ return calculate_keys_custom_exponent(p, q, DEFAULT_EXPONENT)
+
+
+def gen_keys(nbits, getprime_func, accurate=True, exponent=DEFAULT_EXPONENT):
+ """Generate RSA keys of nbits bits. Returns (p, q, e, d).
+
+ Note: this can take a long time, depending on the key size.
+
+ :param nbits: the total number of bits in ``p`` and ``q``. Both ``p`` and
+ ``q`` will use ``nbits/2`` bits.
+ :param getprime_func: either :py:func:`rsa.prime.getprime` or a function
+ with similar signature.
+ :param exponent: the exponent for the key; only change this if you know
+ what you're doing, as the exponent influences how difficult your
+ private key can be cracked. A very common choice for e is 65537.
+ :type exponent: int
+ """
+
+ # Regenerate p and q values, until calculate_keys doesn't raise a
+ # ValueError.
+ while True:
+ (p, q) = find_p_q(nbits // 2, getprime_func, accurate)
+ try:
+ (e, d) = calculate_keys_custom_exponent(p, q, exponent=exponent)
+ break
+ except ValueError:
+ pass
+
+ return p, q, e, d
+
+
+def newkeys(nbits, accurate=True, poolsize=1, exponent=DEFAULT_EXPONENT):
+ """Generates public and private keys, and returns them as (pub, priv).
+
+ The public key is also known as the 'encryption key', and is a
+ :py:class:`rsa.PublicKey` object. The private key is also known as the
+ 'decryption key' and is a :py:class:`rsa.PrivateKey` object.
+
+ :param nbits: the number of bits required to store ``n = p*q``.
+ :param accurate: when True, ``n`` will have exactly the number of bits you
+ asked for. However, this makes key generation much slower. When False,
+ `n`` may have slightly less bits.
+ :param poolsize: the number of processes to use to generate the prime
+ numbers. If set to a number > 1, a parallel algorithm will be used.
+ This requires Python 2.6 or newer.
+ :param exponent: the exponent for the key; only change this if you know
+ what you're doing, as the exponent influences how difficult your
+ private key can be cracked. A very common choice for e is 65537.
+ :type exponent: int
+
+ :returns: a tuple (:py:class:`rsa.PublicKey`, :py:class:`rsa.PrivateKey`)
+
+ The ``poolsize`` parameter was added in *Python-RSA 3.1* and requires
+ Python 2.6 or newer.
+
+ """
+
+ if nbits < 16:
+ raise ValueError('Key too small')
+
+ if poolsize < 1:
+ raise ValueError('Pool size (%i) should be >= 1' % poolsize)
+
+ # Determine which getprime function to use
+ if poolsize > 1:
+ from rsa import parallel
+ import functools
+
+ getprime_func = functools.partial(parallel.getprime, poolsize=poolsize)
+ else:
+ getprime_func = rsa.prime.getprime
+
+ # Generate the key components
+ (p, q, e, d) = gen_keys(nbits, getprime_func, accurate=accurate, exponent=exponent)
+
+ # Create the key objects
+ n = p * q
+
+ return (
+ PublicKey(n, e),
+ PrivateKey(n, e, d, p, q)
+ )
+
+
+__all__ = ['PublicKey', 'PrivateKey', 'newkeys']
+
+if __name__ == '__main__':
+ import doctest
+
+ try:
+ for count in range(100):
+ (failures, tests) = doctest.testmod()
+ if failures:
+ break
+
+ if (count % 10 == 0 and count) or count == 1:
+ print('%i times' % count)
+ except KeyboardInterrupt:
+ print('Aborted')
+ else:
+ print('Doctests done')
diff --git a/contrib/python/rsa/py2/rsa/machine_size.py b/contrib/python/rsa/py2/rsa/machine_size.py
new file mode 100644
index 0000000000..2a871b8f6e
--- /dev/null
+++ b/contrib/python/rsa/py2/rsa/machine_size.py
@@ -0,0 +1,74 @@
+# -*- 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.
+
+"""Detection of 32-bit and 64-bit machines and byte alignment."""
+
+import sys
+
+MAX_INT = sys.maxsize
+MAX_INT64 = (1 << 63) - 1
+MAX_INT32 = (1 << 31) - 1
+MAX_INT16 = (1 << 15) - 1
+
+# Determine the word size of the processor.
+if MAX_INT == MAX_INT64:
+ # 64-bit processor.
+ MACHINE_WORD_SIZE = 64
+elif MAX_INT == MAX_INT32:
+ # 32-bit processor.
+ MACHINE_WORD_SIZE = 32
+else:
+ # Else we just assume 64-bit processor keeping up with modern times.
+ MACHINE_WORD_SIZE = 64
+
+
+def get_word_alignment(num, force_arch=64,
+ _machine_word_size=MACHINE_WORD_SIZE):
+ """
+ Returns alignment details for the given number based on the platform
+ Python is running on.
+
+ :param num:
+ Unsigned integral number.
+ :param force_arch:
+ If you don't want to use 64-bit unsigned chunks, set this to
+ anything other than 64. 32-bit chunks will be preferred then.
+ Default 64 will be used when on a 64-bit machine.
+ :param _machine_word_size:
+ (Internal) The machine word size used for alignment.
+ :returns:
+ 4-tuple::
+
+ (word_bits, word_bytes,
+ max_uint, packing_format_type)
+ """
+ max_uint64 = 0xffffffffffffffff
+ max_uint32 = 0xffffffff
+ max_uint16 = 0xffff
+ max_uint8 = 0xff
+
+ if force_arch == 64 and _machine_word_size >= 64 and num > max_uint32:
+ # 64-bit unsigned integer.
+ return 64, 8, max_uint64, "Q"
+ elif num > max_uint16:
+ # 32-bit unsigned integer
+ return 32, 4, max_uint32, "L"
+ elif num > max_uint8:
+ # 16-bit unsigned integer.
+ return 16, 2, max_uint16, "H"
+ else:
+ # 8-bit unsigned integer.
+ return 8, 1, max_uint8, "B"
diff --git a/contrib/python/rsa/py2/rsa/parallel.py b/contrib/python/rsa/py2/rsa/parallel.py
new file mode 100644
index 0000000000..a3fe312204
--- /dev/null
+++ b/contrib/python/rsa/py2/rsa/parallel.py
@@ -0,0 +1,101 @@
+# -*- 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.
+
+"""Functions for parallel computation on multiple cores.
+
+Introduced in Python-RSA 3.1.
+
+.. note::
+
+ Requires Python 2.6 or newer.
+
+"""
+
+from __future__ import print_function
+
+import multiprocessing as mp
+
+from rsa._compat import range
+import rsa.prime
+import rsa.randnum
+
+
+def _find_prime(nbits, pipe):
+ while True:
+ integer = rsa.randnum.read_random_odd_int(nbits)
+
+ # Test for primeness
+ if rsa.prime.is_prime(integer):
+ pipe.send(integer)
+ return
+
+
+def getprime(nbits, poolsize):
+ """Returns a prime number that can be stored in 'nbits' bits.
+
+ Works in multiple threads at the same time.
+
+ >>> p = getprime(128, 3)
+ >>> rsa.prime.is_prime(p-1)
+ False
+ >>> rsa.prime.is_prime(p)
+ True
+ >>> rsa.prime.is_prime(p+1)
+ False
+
+ >>> from rsa import common
+ >>> common.bit_size(p) == 128
+ True
+
+ """
+
+ (pipe_recv, pipe_send) = mp.Pipe(duplex=False)
+
+ # Create processes
+ try:
+ procs = [mp.Process(target=_find_prime, args=(nbits, pipe_send))
+ for _ in range(poolsize)]
+ # Start processes
+ for p in procs:
+ p.start()
+
+ result = pipe_recv.recv()
+ finally:
+ pipe_recv.close()
+ pipe_send.close()
+
+ # Terminate processes
+ for p in procs:
+ p.terminate()
+
+ return result
+
+
+__all__ = ['getprime']
+
+if __name__ == '__main__':
+ print('Running doctests 1000x or until failure')
+ import doctest
+
+ for count in range(100):
+ (failures, tests) = doctest.testmod()
+ if failures:
+ break
+
+ if count % 10 == 0 and count:
+ print('%i times' % count)
+
+ print('Doctests done')
diff --git a/contrib/python/rsa/py2/rsa/pem.py b/contrib/python/rsa/py2/rsa/pem.py
new file mode 100644
index 0000000000..2ddfae86e2
--- /dev/null
+++ b/contrib/python/rsa/py2/rsa/pem.py
@@ -0,0 +1,126 @@
+# -*- 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.
+
+"""Functions that load and write PEM-encoded files."""
+
+import base64
+
+from rsa._compat import is_bytes, range
+
+
+def _markers(pem_marker):
+ """
+ Returns the start and end PEM markers, as bytes.
+ """
+
+ if not is_bytes(pem_marker):
+ pem_marker = pem_marker.encode('ascii')
+
+ return (b'-----BEGIN ' + pem_marker + b'-----',
+ b'-----END ' + pem_marker + b'-----')
+
+
+def load_pem(contents, pem_marker):
+ """Loads a PEM file.
+
+ :param contents: the contents of the file to interpret
+ :param pem_marker: the marker of the PEM content, such as 'RSA PRIVATE KEY'
+ when your file has '-----BEGIN RSA PRIVATE KEY-----' and
+ '-----END RSA PRIVATE KEY-----' markers.
+
+ :return: the base64-decoded content between the start and end markers.
+
+ @raise ValueError: when the content is invalid, for example when the start
+ marker cannot be found.
+
+ """
+
+ # We want bytes, not text. If it's text, it can be converted to ASCII bytes.
+ if not is_bytes(contents):
+ contents = contents.encode('ascii')
+
+ (pem_start, pem_end) = _markers(pem_marker)
+
+ pem_lines = []
+ in_pem_part = False
+
+ for line in contents.splitlines():
+ line = line.strip()
+
+ # Skip empty lines
+ if not line:
+ continue
+
+ # Handle start marker
+ if line == pem_start:
+ if in_pem_part:
+ raise ValueError('Seen start marker "%s" twice' % pem_start)
+
+ in_pem_part = True
+ continue
+
+ # Skip stuff before first marker
+ if not in_pem_part:
+ continue
+
+ # Handle end marker
+ if in_pem_part and line == pem_end:
+ in_pem_part = False
+ break
+
+ # Load fields
+ if b':' in line:
+ continue
+
+ pem_lines.append(line)
+
+ # Do some sanity checks
+ if not pem_lines:
+ raise ValueError('No PEM start marker "%s" found' % pem_start)
+
+ if in_pem_part:
+ raise ValueError('No PEM end marker "%s" found' % pem_end)
+
+ # Base64-decode the contents
+ pem = b''.join(pem_lines)
+ return base64.standard_b64decode(pem)
+
+
+def save_pem(contents, pem_marker):
+ """Saves a PEM file.
+
+ :param contents: the contents to encode in PEM format
+ :param pem_marker: the marker of the PEM content, such as 'RSA PRIVATE KEY'
+ when your file has '-----BEGIN RSA PRIVATE KEY-----' and
+ '-----END RSA PRIVATE KEY-----' markers.
+
+ :return: the base64-encoded content between the start and end markers, as bytes.
+
+ """
+
+ (pem_start, pem_end) = _markers(pem_marker)
+
+ b64 = base64.standard_b64encode(contents).replace(b'\n', b'')
+ pem_lines = [pem_start]
+
+ for block_start in range(0, len(b64), 64):
+ block = b64[block_start:block_start + 64]
+ pem_lines.append(block)
+
+ pem_lines.append(pem_end)
+ pem_lines.append(b'')
+
+ return b'\n'.join(pem_lines)
diff --git a/contrib/python/rsa/py2/rsa/pkcs1.py b/contrib/python/rsa/py2/rsa/pkcs1.py
new file mode 100644
index 0000000000..c05239afce
--- /dev/null
+++ b/contrib/python/rsa/py2/rsa/pkcs1.py
@@ -0,0 +1,448 @@
+# -*- 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.
+
+"""Functions for PKCS#1 version 1.5 encryption and signing
+
+This module implements certain functionality from PKCS#1 version 1.5. For a
+very clear example, read http://www.di-mgt.com.au/rsa_alg.html#pkcs1schemes
+
+At least 8 bytes of random padding is used when encrypting a message. This makes
+these methods much more secure than the ones in the ``rsa`` module.
+
+WARNING: this module leaks information when decryption fails. The exceptions
+that are raised contain the Python traceback information, which can be used to
+deduce where in the process the failure occurred. DO NOT PASS SUCH INFORMATION
+to your users.
+"""
+
+import hashlib
+import os
+
+from rsa._compat import range
+from rsa import common, transform, core
+
+# ASN.1 codes that describe the hash algorithm used.
+HASH_ASN1 = {
+ 'MD5': b'\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10',
+ 'SHA-1': b'\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14',
+ 'SHA-224': b'\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c',
+ 'SHA-256': b'\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20',
+ 'SHA-384': b'\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30',
+ 'SHA-512': b'\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40',
+}
+
+HASH_METHODS = {
+ 'MD5': hashlib.md5,
+ 'SHA-1': hashlib.sha1,
+ 'SHA-224': hashlib.sha224,
+ 'SHA-256': hashlib.sha256,
+ 'SHA-384': hashlib.sha384,
+ 'SHA-512': hashlib.sha512,
+}
+
+
+class CryptoError(Exception):
+ """Base class for all exceptions in this module."""
+
+
+class DecryptionError(CryptoError):
+ """Raised when decryption fails."""
+
+
+class VerificationError(CryptoError):
+ """Raised when verification fails."""
+
+
+def _pad_for_encryption(message, target_length):
+ r"""Pads the message for encryption, returning the padded message.
+
+ :return: 00 02 RANDOM_DATA 00 MESSAGE
+
+ >>> block = _pad_for_encryption(b'hello', 16)
+ >>> len(block)
+ 16
+ >>> block[0:2]
+ b'\x00\x02'
+ >>> block[-6:]
+ b'\x00hello'
+
+ """
+
+ max_msglength = target_length - 11
+ msglength = len(message)
+
+ if msglength > max_msglength:
+ raise OverflowError('%i bytes needed for message, but there is only'
+ ' space for %i' % (msglength, max_msglength))
+
+ # Get random padding
+ padding = b''
+ padding_length = target_length - msglength - 3
+
+ # We remove 0-bytes, so we'll end up with less padding than we've asked for,
+ # so keep adding data until we're at the correct length.
+ while len(padding) < padding_length:
+ needed_bytes = padding_length - len(padding)
+
+ # Always read at least 8 bytes more than we need, and trim off the rest
+ # after removing the 0-bytes. This increases the chance of getting
+ # enough bytes, especially when needed_bytes is small
+ new_padding = os.urandom(needed_bytes + 5)
+ new_padding = new_padding.replace(b'\x00', b'')
+ padding = padding + new_padding[:needed_bytes]
+
+ assert len(padding) == padding_length
+
+ return b''.join([b'\x00\x02',
+ padding,
+ b'\x00',
+ message])
+
+
+def _pad_for_signing(message, target_length):
+ r"""Pads the message for signing, returning the padded message.
+
+ The padding is always a repetition of FF bytes.
+
+ :return: 00 01 PADDING 00 MESSAGE
+
+ >>> block = _pad_for_signing(b'hello', 16)
+ >>> len(block)
+ 16
+ >>> block[0:2]
+ b'\x00\x01'
+ >>> block[-6:]
+ b'\x00hello'
+ >>> block[2:-6]
+ b'\xff\xff\xff\xff\xff\xff\xff\xff'
+
+ """
+
+ max_msglength = target_length - 11
+ msglength = len(message)
+
+ if msglength > max_msglength:
+ raise OverflowError('%i bytes needed for message, but there is only'
+ ' space for %i' % (msglength, max_msglength))
+
+ padding_length = target_length - msglength - 3
+
+ return b''.join([b'\x00\x01',
+ padding_length * b'\xff',
+ b'\x00',
+ message])
+
+
+def encrypt(message, pub_key):
+ """Encrypts the given message using PKCS#1 v1.5
+
+ :param message: the message to encrypt. Must be a byte string no longer than
+ ``k-11`` bytes, where ``k`` is the number of bytes needed to encode
+ the ``n`` component of the public key.
+ :param pub_key: the :py:class:`rsa.PublicKey` to encrypt with.
+ :raise OverflowError: when the message is too large to fit in the padded
+ block.
+
+ >>> from rsa import key, common
+ >>> (pub_key, priv_key) = key.newkeys(256)
+ >>> message = b'hello'
+ >>> crypto = encrypt(message, pub_key)
+
+ The crypto text should be just as long as the public key 'n' component:
+
+ >>> len(crypto) == common.byte_size(pub_key.n)
+ True
+
+ """
+
+ keylength = common.byte_size(pub_key.n)
+ padded = _pad_for_encryption(message, keylength)
+
+ payload = transform.bytes2int(padded)
+ encrypted = core.encrypt_int(payload, pub_key.e, pub_key.n)
+ block = transform.int2bytes(encrypted, keylength)
+
+ return block
+
+
+def decrypt(crypto, priv_key):
+ r"""Decrypts the given message using PKCS#1 v1.5
+
+ The decryption is considered 'failed' when the resulting cleartext doesn't
+ start with the bytes 00 02, or when the 00 byte between the padding and
+ the message cannot be found.
+
+ :param crypto: the crypto text as returned by :py:func:`rsa.encrypt`
+ :param priv_key: the :py:class:`rsa.PrivateKey` to decrypt with.
+ :raise DecryptionError: when the decryption fails. No details are given as
+ to why the code thinks the decryption fails, as this would leak
+ information about the private key.
+
+
+ >>> import rsa
+ >>> (pub_key, priv_key) = rsa.newkeys(256)
+
+ It works with strings:
+
+ >>> crypto = encrypt(b'hello', pub_key)
+ >>> decrypt(crypto, priv_key)
+ b'hello'
+
+ And with binary data:
+
+ >>> crypto = encrypt(b'\x00\x00\x00\x00\x01', pub_key)
+ >>> decrypt(crypto, priv_key)
+ b'\x00\x00\x00\x00\x01'
+
+ Altering the encrypted information will *likely* cause a
+ :py:class:`rsa.pkcs1.DecryptionError`. If you want to be *sure*, use
+ :py:func:`rsa.sign`.
+
+
+ .. warning::
+
+ Never display the stack trace of a
+ :py:class:`rsa.pkcs1.DecryptionError` exception. It shows where in the
+ code the exception occurred, and thus leaks information about the key.
+ It's only a tiny bit of information, but every bit makes cracking the
+ keys easier.
+
+ >>> crypto = encrypt(b'hello', pub_key)
+ >>> crypto = crypto[0:5] + b'X' + crypto[6:] # change a byte
+ >>> decrypt(crypto, priv_key)
+ Traceback (most recent call last):
+ ...
+ rsa.pkcs1.DecryptionError: Decryption failed
+
+ """
+
+ blocksize = common.byte_size(priv_key.n)
+ encrypted = transform.bytes2int(crypto)
+ decrypted = priv_key.blinded_decrypt(encrypted)
+ cleartext = transform.int2bytes(decrypted, blocksize)
+
+ # Detect leading zeroes in the crypto. These are not reflected in the
+ # encrypted value (as leading zeroes do not influence the value of an
+ # integer). This fixes CVE-2020-13757.
+ if len(crypto) > blocksize:
+ raise DecryptionError('Decryption failed')
+
+ # If we can't find the cleartext marker, decryption failed.
+ if cleartext[0:2] != b'\x00\x02':
+ raise DecryptionError('Decryption failed')
+
+ # Find the 00 separator between the padding and the message
+ try:
+ sep_idx = cleartext.index(b'\x00', 2)
+ except ValueError:
+ raise DecryptionError('Decryption failed')
+
+ return cleartext[sep_idx + 1:]
+
+
+def sign_hash(hash_value, priv_key, hash_method):
+ """Signs a precomputed hash with the private key.
+
+ Hashes the message, then signs the hash with the given key. This is known
+ as a "detached signature", because the message itself isn't altered.
+
+ :param hash_value: A precomputed hash to sign (ignores message). Should be set to
+ None if needing to hash and sign message.
+ :param priv_key: the :py:class:`rsa.PrivateKey` to sign with
+ :param hash_method: the hash method used on the message. Use 'MD5', 'SHA-1',
+ 'SHA-224', SHA-256', 'SHA-384' or 'SHA-512'.
+ :return: a message signature block.
+ :raise OverflowError: if the private key is too small to contain the
+ requested hash.
+
+ """
+
+ # Get the ASN1 code for this hash method
+ if hash_method not in HASH_ASN1:
+ raise ValueError('Invalid hash method: %s' % hash_method)
+ asn1code = HASH_ASN1[hash_method]
+
+ # Encrypt the hash with the private key
+ cleartext = asn1code + hash_value
+ keylength = common.byte_size(priv_key.n)
+ padded = _pad_for_signing(cleartext, keylength)
+
+ payload = transform.bytes2int(padded)
+ encrypted = priv_key.blinded_encrypt(payload)
+ block = transform.int2bytes(encrypted, keylength)
+
+ return block
+
+
+def sign(message, priv_key, hash_method):
+ """Signs the message with the private key.
+
+ Hashes the message, then signs the hash with the given key. This is known
+ as a "detached signature", because the message itself isn't altered.
+
+ :param message: the message to sign. Can be an 8-bit string or a file-like
+ object. If ``message`` has a ``read()`` method, it is assumed to be a
+ file-like object.
+ :param priv_key: the :py:class:`rsa.PrivateKey` to sign with
+ :param hash_method: the hash method used on the message. Use 'MD5', 'SHA-1',
+ 'SHA-224', SHA-256', 'SHA-384' or 'SHA-512'.
+ :return: a message signature block.
+ :raise OverflowError: if the private key is too small to contain the
+ requested hash.
+
+ """
+
+ msg_hash = compute_hash(message, hash_method)
+ return sign_hash(msg_hash, priv_key, hash_method)
+
+
+def verify(message, signature, pub_key):
+ """Verifies that the signature matches the message.
+
+ The hash method is detected automatically from the signature.
+
+ :param message: the signed message. Can be an 8-bit string or a file-like
+ object. If ``message`` has a ``read()`` method, it is assumed to be a
+ file-like object.
+ :param signature: the signature block, as created with :py:func:`rsa.sign`.
+ :param pub_key: the :py:class:`rsa.PublicKey` of the person signing the message.
+ :raise VerificationError: when the signature doesn't match the message.
+ :returns: the name of the used hash.
+
+ """
+
+ keylength = common.byte_size(pub_key.n)
+ encrypted = transform.bytes2int(signature)
+ decrypted = core.decrypt_int(encrypted, pub_key.e, pub_key.n)
+ clearsig = transform.int2bytes(decrypted, keylength)
+
+ # Get the hash method
+ method_name = _find_method_hash(clearsig)
+ message_hash = compute_hash(message, method_name)
+
+ # Reconstruct the expected padded hash
+ cleartext = HASH_ASN1[method_name] + message_hash
+ expected = _pad_for_signing(cleartext, keylength)
+
+ if len(signature) != keylength:
+ raise VerificationError('Verification failed')
+
+ # Compare with the signed one
+ if expected != clearsig:
+ raise VerificationError('Verification failed')
+
+ return method_name
+
+
+def find_signature_hash(signature, pub_key):
+ """Returns the hash name detected from the signature.
+
+ If you also want to verify the message, use :py:func:`rsa.verify()` instead.
+ It also returns the name of the used hash.
+
+ :param signature: the signature block, as created with :py:func:`rsa.sign`.
+ :param pub_key: the :py:class:`rsa.PublicKey` of the person signing the message.
+ :returns: the name of the used hash.
+ """
+
+ keylength = common.byte_size(pub_key.n)
+ encrypted = transform.bytes2int(signature)
+ decrypted = core.decrypt_int(encrypted, pub_key.e, pub_key.n)
+ clearsig = transform.int2bytes(decrypted, keylength)
+
+ return _find_method_hash(clearsig)
+
+
+def yield_fixedblocks(infile, blocksize):
+ """Generator, yields each block of ``blocksize`` bytes in the input file.
+
+ :param infile: file to read and separate in blocks.
+ :param blocksize: block size in bytes.
+ :returns: a generator that yields the contents of each block
+ """
+
+ while True:
+ block = infile.read(blocksize)
+
+ read_bytes = len(block)
+ if read_bytes == 0:
+ break
+
+ yield block
+
+ if read_bytes < blocksize:
+ break
+
+
+def compute_hash(message, method_name):
+ """Returns the message digest.
+
+ :param message: the signed message. Can be an 8-bit string or a file-like
+ object. If ``message`` has a ``read()`` method, it is assumed to be a
+ file-like object.
+ :param method_name: the hash method, must be a key of
+ :py:const:`HASH_METHODS`.
+
+ """
+
+ if method_name not in HASH_METHODS:
+ raise ValueError('Invalid hash method: %s' % method_name)
+
+ method = HASH_METHODS[method_name]
+ hasher = method()
+
+ if hasattr(message, 'read') and hasattr(message.read, '__call__'):
+ # read as 1K blocks
+ for block in yield_fixedblocks(message, 1024):
+ hasher.update(block)
+ else:
+ # hash the message object itself.
+ hasher.update(message)
+
+ return hasher.digest()
+
+
+def _find_method_hash(clearsig):
+ """Finds the hash method.
+
+ :param clearsig: full padded ASN1 and hash.
+ :return: the used hash method.
+ :raise VerificationFailed: when the hash method cannot be found
+ """
+
+ for (hashname, asn1code) in HASH_ASN1.items():
+ if asn1code in clearsig:
+ return hashname
+
+ raise VerificationError('Verification failed')
+
+
+__all__ = ['encrypt', 'decrypt', 'sign', 'verify',
+ 'DecryptionError', 'VerificationError', 'CryptoError']
+
+if __name__ == '__main__':
+ print('Running doctests 1000x or until failure')
+ import doctest
+
+ for count in range(1000):
+ (failures, tests) = doctest.testmod()
+ if failures:
+ break
+
+ if count % 100 == 0 and count:
+ print('%i times' % count)
+
+ print('Doctests done')
diff --git a/contrib/python/rsa/py2/rsa/pkcs1_v2.py b/contrib/python/rsa/py2/rsa/pkcs1_v2.py
new file mode 100644
index 0000000000..5f9c7ddcea
--- /dev/null
+++ b/contrib/python/rsa/py2/rsa/pkcs1_v2.py
@@ -0,0 +1,103 @@
+# -*- 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.
+
+"""Functions for PKCS#1 version 2 encryption and signing
+
+This module implements certain functionality from PKCS#1 version 2. Main
+documentation is RFC 2437: https://tools.ietf.org/html/rfc2437
+"""
+
+from rsa._compat import range
+from rsa import (
+ common,
+ pkcs1,
+ transform,
+)
+
+
+def mgf1(seed, length, hasher='SHA-1'):
+ """
+ MGF1 is a Mask Generation Function based on a hash function.
+
+ A mask generation function takes an octet string of variable length and a
+ desired output length as input, and outputs an octet string of the desired
+ length. The plaintext-awareness of RSAES-OAEP relies on the random nature of
+ the output of the mask generation function, which in turn relies on the
+ random nature of the underlying hash.
+
+ :param bytes seed: seed from which mask is generated, an octet string
+ :param int length: intended length in octets of the mask, at most 2^32(hLen)
+ :param str hasher: hash function (hLen denotes the length in octets of the hash
+ function output)
+
+ :return: mask, an octet string of length `length`
+ :rtype: bytes
+
+ :raise OverflowError: when `length` is too large for the specified `hasher`
+ :raise ValueError: when specified `hasher` is invalid
+ """
+
+ try:
+ hash_length = pkcs1.HASH_METHODS[hasher]().digest_size
+ except KeyError:
+ raise ValueError(
+ 'Invalid `hasher` specified. Please select one of: {hash_list}'.format(
+ hash_list=', '.join(sorted(pkcs1.HASH_METHODS.keys()))
+ )
+ )
+
+ # If l > 2^32(hLen), output "mask too long" and stop.
+ if length > (2**32 * hash_length):
+ raise OverflowError(
+ "Desired length should be at most 2**32 times the hasher's output "
+ "length ({hash_length} for {hasher} function)".format(
+ hash_length=hash_length,
+ hasher=hasher,
+ )
+ )
+
+ # Looping `counter` from 0 to ceil(l / hLen)-1, build `output` based on the
+ # hashes formed by (`seed` + C), being `C` an octet string of length 4
+ # generated by converting `counter` with the primitive I2OSP
+ output = b''.join(
+ pkcs1.compute_hash(
+ seed + transform.int2bytes(counter, fill_size=4),
+ method_name=hasher,
+ )
+ for counter in range(common.ceil_div(length, hash_length) + 1)
+ )
+
+ # Output the leading `length` octets of `output` as the octet string mask.
+ return output[:length]
+
+
+__all__ = [
+ 'mgf1',
+]
+
+if __name__ == '__main__':
+ print('Running doctests 1000x or until failure')
+ import doctest
+
+ for count in range(1000):
+ (failures, tests) = doctest.testmod()
+ if failures:
+ break
+
+ if count % 100 == 0 and count:
+ print('%i times' % count)
+
+ print('Doctests done')
diff --git a/contrib/python/rsa/py2/rsa/prime.py b/contrib/python/rsa/py2/rsa/prime.py
new file mode 100644
index 0000000000..3d63542e66
--- /dev/null
+++ b/contrib/python/rsa/py2/rsa/prime.py
@@ -0,0 +1,201 @@
+# -*- 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.
+
+"""Numerical functions related to primes.
+
+Implementation based on the book Algorithm Design by Michael T. Goodrich and
+Roberto Tamassia, 2002.
+"""
+
+from rsa._compat import range
+import rsa.common
+import rsa.randnum
+
+__all__ = ['getprime', 'are_relatively_prime']
+
+
+def gcd(p, q):
+ """Returns the greatest common divisor of p and q
+
+ >>> gcd(48, 180)
+ 12
+ """
+
+ while q != 0:
+ (p, q) = (q, p % q)
+ return p
+
+
+def get_primality_testing_rounds(number):
+ """Returns minimum number of rounds for Miller-Rabing primality testing,
+ based on number bitsize.
+
+ According to NIST FIPS 186-4, Appendix C, Table C.3, minimum number of
+ rounds of M-R testing, using an error probability of 2 ** (-100), for
+ different p, q bitsizes are:
+ * p, q bitsize: 512; rounds: 7
+ * p, q bitsize: 1024; rounds: 4
+ * p, q bitsize: 1536; rounds: 3
+ See: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
+ """
+
+ # Calculate number bitsize.
+ bitsize = rsa.common.bit_size(number)
+ # Set number of rounds.
+ if bitsize >= 1536:
+ return 3
+ if bitsize >= 1024:
+ return 4
+ if bitsize >= 512:
+ return 7
+ # For smaller bitsizes, set arbitrary number of rounds.
+ return 10
+
+
+def miller_rabin_primality_testing(n, k):
+ """Calculates whether n is composite (which is always correct) or prime
+ (which theoretically is incorrect with error probability 4**-k), by
+ applying Miller-Rabin primality testing.
+
+ For reference and implementation example, see:
+ https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test
+
+ :param n: Integer to be tested for primality.
+ :type n: int
+ :param k: Number of rounds (witnesses) of Miller-Rabin testing.
+ :type k: int
+ :return: False if the number is composite, True if it's probably prime.
+ :rtype: bool
+ """
+
+ # prevent potential infinite loop when d = 0
+ if n < 2:
+ return False
+
+ # Decompose (n - 1) to write it as (2 ** r) * d
+ # While d is even, divide it by 2 and increase the exponent.
+ d = n - 1
+ r = 0
+
+ while not (d & 1):
+ r += 1
+ d >>= 1
+
+ # Test k witnesses.
+ for _ in range(k):
+ # Generate random integer a, where 2 <= a <= (n - 2)
+ a = rsa.randnum.randint(n - 3) + 1
+
+ x = pow(a, d, n)
+ if x == 1 or x == n - 1:
+ continue
+
+ for _ in range(r - 1):
+ x = pow(x, 2, n)
+ if x == 1:
+ # n is composite.
+ return False
+ if x == n - 1:
+ # Exit inner loop and continue with next witness.
+ break
+ else:
+ # If loop doesn't break, n is composite.
+ return False
+
+ return True
+
+
+def is_prime(number):
+ """Returns True if the number is prime, and False otherwise.
+
+ >>> is_prime(2)
+ True
+ >>> is_prime(42)
+ False
+ >>> is_prime(41)
+ True
+ """
+
+ # Check for small numbers.
+ if number < 10:
+ return number in {2, 3, 5, 7}
+
+ # Check for even numbers.
+ if not (number & 1):
+ return False
+
+ # Calculate minimum number of rounds.
+ k = get_primality_testing_rounds(number)
+
+ # Run primality testing with (minimum + 1) rounds.
+ return miller_rabin_primality_testing(number, k + 1)
+
+
+def getprime(nbits):
+ """Returns a prime number that can be stored in 'nbits' bits.
+
+ >>> p = getprime(128)
+ >>> is_prime(p-1)
+ False
+ >>> is_prime(p)
+ True
+ >>> is_prime(p+1)
+ False
+
+ >>> from rsa import common
+ >>> common.bit_size(p) == 128
+ True
+ """
+
+ assert nbits > 3 # the loop wil hang on too small numbers
+
+ while True:
+ integer = rsa.randnum.read_random_odd_int(nbits)
+
+ # Test for primeness
+ if is_prime(integer):
+ return integer
+
+ # Retry if not prime
+
+
+def are_relatively_prime(a, b):
+ """Returns True if a and b are relatively prime, and False if they
+ are not.
+
+ >>> are_relatively_prime(2, 3)
+ True
+ >>> are_relatively_prime(2, 4)
+ False
+ """
+
+ d = gcd(a, b)
+ return d == 1
+
+
+if __name__ == '__main__':
+ print('Running doctests 1000x or until failure')
+ import doctest
+
+ for count in range(1000):
+ (failures, tests) = doctest.testmod()
+ if failures:
+ break
+
+ if count % 100 == 0 and count:
+ print('%i times' % count)
+
+ print('Doctests done')
diff --git a/contrib/python/rsa/py2/rsa/randnum.py b/contrib/python/rsa/py2/rsa/randnum.py
new file mode 100644
index 0000000000..310acaa620
--- /dev/null
+++ b/contrib/python/rsa/py2/rsa/randnum.py
@@ -0,0 +1,98 @@
+# -*- 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.
+
+"""Functions for generating random numbers."""
+
+# Source inspired by code by Yesudeep Mangalapilly <yesudeep@gmail.com>
+
+import os
+
+from rsa import common, transform
+from rsa._compat import byte
+
+
+def read_random_bits(nbits):
+ """Reads 'nbits' random bits.
+
+ If nbits isn't a whole number of bytes, an extra byte will be appended with
+ only the lower bits set.
+ """
+
+ nbytes, rbits = divmod(nbits, 8)
+
+ # Get the random bytes
+ randomdata = os.urandom(nbytes)
+
+ # Add the remaining random bits
+ if rbits > 0:
+ randomvalue = ord(os.urandom(1))
+ randomvalue >>= (8 - rbits)
+ randomdata = byte(randomvalue) + randomdata
+
+ return randomdata
+
+
+def read_random_int(nbits):
+ """Reads a random integer of approximately nbits bits.
+ """
+
+ randomdata = read_random_bits(nbits)
+ value = transform.bytes2int(randomdata)
+
+ # Ensure that the number is large enough to just fill out the required
+ # number of bits.
+ value |= 1 << (nbits - 1)
+
+ return value
+
+
+def read_random_odd_int(nbits):
+ """Reads a random odd integer of approximately nbits bits.
+
+ >>> read_random_odd_int(512) & 1
+ 1
+ """
+
+ value = read_random_int(nbits)
+
+ # Make sure it's odd
+ return value | 1
+
+
+def randint(maxvalue):
+ """Returns a random integer x with 1 <= x <= maxvalue
+
+ May take a very long time in specific situations. If maxvalue needs N bits
+ to store, the closer maxvalue is to (2 ** N) - 1, the faster this function
+ is.
+ """
+
+ bit_size = common.bit_size(maxvalue)
+
+ tries = 0
+ while True:
+ value = read_random_int(bit_size)
+ if value <= maxvalue:
+ break
+
+ if tries % 10 == 0 and tries:
+ # After a lot of tries to get the right number of bits but still
+ # smaller than maxvalue, decrease the number of bits by 1. That'll
+ # dramatically increase the chances to get a large enough number.
+ bit_size -= 1
+ tries += 1
+
+ return value
diff --git a/contrib/python/rsa/py2/rsa/transform.py b/contrib/python/rsa/py2/rsa/transform.py
new file mode 100644
index 0000000000..628d0afb55
--- /dev/null
+++ b/contrib/python/rsa/py2/rsa/transform.py
@@ -0,0 +1,215 @@
+# -*- 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.
+
+"""Data transformation functions.
+
+From bytes to a number, number to bytes, etc.
+"""
+
+from __future__ import absolute_import
+
+import binascii
+from struct import pack
+
+from rsa._compat import byte, is_integer
+from rsa import common, machine_size
+
+
+def bytes2int(raw_bytes):
+ r"""Converts a list of bytes or an 8-bit string to an integer.
+
+ When using unicode strings, encode it to some encoding like UTF8 first.
+
+ >>> (((128 * 256) + 64) * 256) + 15
+ 8405007
+ >>> bytes2int(b'\x80@\x0f')
+ 8405007
+
+ """
+
+ return int(binascii.hexlify(raw_bytes), 16)
+
+
+def _int2bytes(number, block_size=None):
+ r"""Converts a number to a string of bytes.
+
+ Usage::
+
+ >>> _int2bytes(123456789)
+ b'\x07[\xcd\x15'
+ >>> bytes2int(_int2bytes(123456789))
+ 123456789
+
+ >>> _int2bytes(123456789, 6)
+ b'\x00\x00\x07[\xcd\x15'
+ >>> bytes2int(_int2bytes(123456789, 128))
+ 123456789
+
+ >>> _int2bytes(123456789, 3)
+ Traceback (most recent call last):
+ ...
+ OverflowError: Needed 4 bytes for number, but block size is 3
+
+ @param number: the number to convert
+ @param block_size: the number of bytes to output. If the number encoded to
+ bytes is less than this, the block will be zero-padded. When not given,
+ the returned block is not padded.
+
+ @throws OverflowError when block_size is given and the number takes up more
+ bytes than fit into the block.
+ """
+
+ # Type checking
+ if not is_integer(number):
+ raise TypeError("You must pass an integer for 'number', not %s" %
+ number.__class__)
+
+ if number < 0:
+ raise ValueError('Negative numbers cannot be used: %i' % number)
+
+ # Do some bounds checking
+ if number == 0:
+ needed_bytes = 1
+ raw_bytes = [b'\x00']
+ else:
+ needed_bytes = common.byte_size(number)
+ raw_bytes = []
+
+ # You cannot compare None > 0 in Python 3x. It will fail with a TypeError.
+ if block_size and block_size > 0:
+ if needed_bytes > block_size:
+ raise OverflowError('Needed %i bytes for number, but block size '
+ 'is %i' % (needed_bytes, block_size))
+
+ # Convert the number to bytes.
+ while number > 0:
+ raw_bytes.insert(0, byte(number & 0xFF))
+ number >>= 8
+
+ # Pad with zeroes to fill the block
+ if block_size and block_size > 0:
+ padding = (block_size - needed_bytes) * b'\x00'
+ else:
+ padding = b''
+
+ return padding + b''.join(raw_bytes)
+
+
+def bytes_leading(raw_bytes, needle=b'\x00'):
+ """
+ Finds the number of prefixed byte occurrences in the haystack.
+
+ Useful when you want to deal with padding.
+
+ :param raw_bytes:
+ Raw bytes.
+ :param needle:
+ The byte to count. Default \x00.
+ :returns:
+ The number of leading needle bytes.
+ """
+
+ leading = 0
+ # Indexing keeps compatibility between Python 2.x and Python 3.x
+ _byte = needle[0]
+ for x in raw_bytes:
+ if x == _byte:
+ leading += 1
+ else:
+ break
+ return leading
+
+
+def int2bytes(number, fill_size=None, chunk_size=None, overflow=False):
+ """
+ Convert an unsigned integer to bytes (base-256 representation)::
+
+ Does not preserve leading zeros if you don't specify a chunk size or
+ fill size.
+
+ .. NOTE:
+ You must not specify both fill_size and chunk_size. Only one
+ of them is allowed.
+
+ :param number:
+ Integer value
+ :param fill_size:
+ If the optional fill size is given the length of the resulting
+ byte string is expected to be the fill size and will be padded
+ with prefix zero bytes to satisfy that length.
+ :param chunk_size:
+ If optional chunk size is given and greater than zero, pad the front of
+ the byte string with binary zeros so that the length is a multiple of
+ ``chunk_size``.
+ :param overflow:
+ ``False`` (default). If this is ``True``, no ``OverflowError``
+ will be raised when the fill_size is shorter than the length
+ of the generated byte sequence. Instead the byte sequence will
+ be returned as is.
+ :returns:
+ Raw bytes (base-256 representation).
+ :raises:
+ ``OverflowError`` when fill_size is given and the number takes up more
+ bytes than fit into the block. This requires the ``overflow``
+ argument to this function to be set to ``False`` otherwise, no
+ error will be raised.
+ """
+
+ if number < 0:
+ raise ValueError("Number must be an unsigned integer: %d" % number)
+
+ if fill_size and chunk_size:
+ raise ValueError("You can either fill or pad chunks, but not both")
+
+ # Ensure these are integers.
+ number & 1
+
+ raw_bytes = b''
+
+ # Pack the integer one machine word at a time into bytes.
+ num = number
+ word_bits, _, max_uint, pack_type = machine_size.get_word_alignment(num)
+ pack_format = ">%s" % pack_type
+ while num > 0:
+ raw_bytes = pack(pack_format, num & max_uint) + raw_bytes
+ num >>= word_bits
+ # Obtain the index of the first non-zero byte.
+ zero_leading = bytes_leading(raw_bytes)
+ if number == 0:
+ raw_bytes = b'\x00'
+ # De-padding.
+ raw_bytes = raw_bytes[zero_leading:]
+
+ length = len(raw_bytes)
+ if fill_size and fill_size > 0:
+ if not overflow and length > fill_size:
+ raise OverflowError(
+ "Need %d bytes for number, but fill size is %d" %
+ (length, fill_size)
+ )
+ raw_bytes = raw_bytes.rjust(fill_size, b'\x00')
+ elif chunk_size and chunk_size > 0:
+ remainder = length % chunk_size
+ if remainder:
+ padding_size = chunk_size - remainder
+ raw_bytes = raw_bytes.rjust(length + padding_size, b'\x00')
+ return raw_bytes
+
+
+if __name__ == '__main__':
+ import doctest
+
+ doctest.testmod()
diff --git a/contrib/python/rsa/py2/rsa/util.py b/contrib/python/rsa/py2/rsa/util.py
new file mode 100644
index 0000000000..29d5eb1218
--- /dev/null
+++ b/contrib/python/rsa/py2/rsa/util.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.
+
+"""Utility functions."""
+
+from __future__ import with_statement, print_function
+
+import sys
+from optparse import OptionParser
+
+import rsa.key
+
+
+def private_to_public():
+ """Reads a private key and outputs the corresponding public key."""
+
+ # Parse the CLI options
+ parser = OptionParser(usage='usage: %prog [options]',
+ description='Reads a private key and outputs the '
+ 'corresponding public key. Both private and public keys use '
+ 'the format described in PKCS#1 v1.5')
+
+ parser.add_option('-i', '--input', dest='infilename', type='string',
+ help='Input filename. Reads from stdin if not specified')
+ parser.add_option('-o', '--output', dest='outfilename', type='string',
+ help='Output filename. Writes to stdout of not specified')
+
+ parser.add_option('--inform', dest='inform',
+ help='key format of input - default PEM',
+ choices=('PEM', 'DER'), default='PEM')
+
+ parser.add_option('--outform', dest='outform',
+ help='key format of output - default PEM',
+ choices=('PEM', 'DER'), default='PEM')
+
+ (cli, cli_args) = parser.parse_args(sys.argv)
+
+ # Read the input data
+ if cli.infilename:
+ print('Reading private key from %s in %s format' %
+ (cli.infilename, cli.inform), file=sys.stderr)
+ with open(cli.infilename, 'rb') as infile:
+ in_data = infile.read()
+ else:
+ print('Reading private key from stdin in %s format' % cli.inform,
+ file=sys.stderr)
+ in_data = sys.stdin.read().encode('ascii')
+
+ assert type(in_data) == bytes, type(in_data)
+
+ # Take the public fields and create a public key
+ priv_key = rsa.key.PrivateKey.load_pkcs1(in_data, cli.inform)
+ pub_key = rsa.key.PublicKey(priv_key.n, priv_key.e)
+
+ # Save to the output file
+ out_data = pub_key.save_pkcs1(cli.outform)
+
+ if cli.outfilename:
+ print('Writing public key to %s in %s format' %
+ (cli.outfilename, cli.outform), file=sys.stderr)
+ with open(cli.outfilename, 'wb') as outfile:
+ outfile.write(out_data)
+ else:
+ print('Writing public key to stdout in %s format' % cli.outform,
+ file=sys.stderr)
+ sys.stdout.write(out_data.decode('ascii'))
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()
diff --git a/contrib/python/rsa/py2/ya.make b/contrib/python/rsa/py2/ya.make
new file mode 100644
index 0000000000..f196afec21
--- /dev/null
+++ b/contrib/python/rsa/py2/ya.make
@@ -0,0 +1,46 @@
+# Generated by devtools/yamaker (pypi).
+
+PY2_LIBRARY()
+
+VERSION(4.5)
+
+LICENSE(Apache-2.0)
+
+PEERDIR(
+ contrib/python/pyasn1
+)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ rsa/__init__.py
+ rsa/_compat.py
+ rsa/asn1.py
+ rsa/cli.py
+ rsa/common.py
+ rsa/core.py
+ rsa/key.py
+ rsa/machine_size.py
+ rsa/parallel.py
+ rsa/pem.py
+ rsa/pkcs1.py
+ rsa/pkcs1_v2.py
+ rsa/prime.py
+ rsa/randnum.py
+ rsa/transform.py
+ rsa/util.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/rsa/py2/
+ .dist-info/METADATA
+ .dist-info/entry_points.txt
+ .dist-info/top_level.txt
+)
+
+END()
+
+RECURSE_FOR_TESTS(
+ tests
+)