Update rsa to 3.4.2
This commit is contained in:
parent
fa65a6dc05
commit
fbe96a0a6d
20 changed files with 871 additions and 786 deletions
|
@ -1,55 +0,0 @@
|
||||||
Python-RSA changelog
|
|
||||||
========================================
|
|
||||||
|
|
||||||
Version 3.1.1 - in development
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
- Fixed doctests for Python 2.7
|
|
||||||
- Removed obsolete unittest so all tests run fine on Python 3.2
|
|
||||||
|
|
||||||
Version 3.1 - released 2012-06-17
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
- Big, big credits to Yesudeep Mangalapilly for all the changes listed
|
|
||||||
below!
|
|
||||||
- Added ability to generate keys on multiple cores simultaneously.
|
|
||||||
- Massive speedup
|
|
||||||
- Partial Python 3.2 compatibility (core functionality works, but
|
|
||||||
saving or loading keys doesn't, for that the pyasn1 package needs to
|
|
||||||
be ported to Python 3 first)
|
|
||||||
- Lots of bug fixes
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Version 3.0.1 - released 2011-08-07
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
- Removed unused import of abc module
|
|
||||||
|
|
||||||
|
|
||||||
Version 3.0 - released 2011-08-05
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
- Changed the meaning of the keysize to mean the size of ``n`` rather than
|
|
||||||
the size of both ``p`` and ``q``. This is the common interpretation of
|
|
||||||
RSA keysize. To get the old behaviour, double the keysize when generating a
|
|
||||||
new key.
|
|
||||||
|
|
||||||
- Added a lot of doctests
|
|
||||||
|
|
||||||
- Added random-padded encryption and decryption using PKCS#1 version 1.5
|
|
||||||
|
|
||||||
- Added hash-based signatures and verification using PKCS#1v1.5
|
|
||||||
|
|
||||||
- Modeling private and public key as real objects rather than dicts.
|
|
||||||
|
|
||||||
- Support for saving and loading keys as PEM and DER files.
|
|
||||||
|
|
||||||
- Ability to extract a public key from a private key (PEM+DER)
|
|
||||||
|
|
||||||
|
|
||||||
Version 2.0
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
- Security improvements by Barry Mead.
|
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
Pure Python RSA implementation
|
|
||||||
==============================
|
|
||||||
|
|
||||||
`Python-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:
|
|
||||||
http://stuvel.eu/rsa
|
|
||||||
|
|
||||||
Download and install using::
|
|
||||||
|
|
||||||
pip install rsa
|
|
||||||
|
|
||||||
or::
|
|
||||||
|
|
||||||
easy_install rsa
|
|
||||||
|
|
||||||
or download it from the `Python Package Index`_.
|
|
||||||
|
|
||||||
The source code is maintained in a `Mercurial repository`_ and is
|
|
||||||
licensed under the `Apache License, version 2.0`_
|
|
||||||
|
|
||||||
|
|
||||||
.. _`Python-RSA`: http://stuvel.eu/rsa
|
|
||||||
.. _`Mercurial repository`: https://bitbucket.org/sybren/python-rsa
|
|
||||||
.. _`Python Package Index`: http://pypi.python.org/pypi/rsa
|
|
||||||
.. _`Apache License, version 2.0`: http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -22,24 +22,21 @@ WARNING: this implementation does not use random padding, compression of the
|
||||||
cleartext input to prevent repetitions, or other common security improvements.
|
cleartext input to prevent repetitions, or other common security improvements.
|
||||||
Use with care.
|
Use with care.
|
||||||
|
|
||||||
If you want to have a more secure implementation, use the functions from the
|
|
||||||
``rsa.pkcs1`` module.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__author__ = "Sybren Stuvel, Barry Mead and Yesudeep Mangalapilly"
|
|
||||||
__date__ = "2015-11-05"
|
|
||||||
__version__ = '3.2.3'
|
|
||||||
|
|
||||||
from rsa.key import newkeys, PrivateKey, PublicKey
|
from rsa.key import newkeys, PrivateKey, PublicKey
|
||||||
from rsa.pkcs1 import encrypt, decrypt, sign, verify, DecryptionError, \
|
from rsa.pkcs1 import encrypt, decrypt, sign, verify, DecryptionError, \
|
||||||
VerificationError
|
VerificationError
|
||||||
|
|
||||||
|
__author__ = "Sybren Stuvel, Barry Mead and Yesudeep Mangalapilly"
|
||||||
|
__date__ = "2016-03-29"
|
||||||
|
__version__ = '3.4.2'
|
||||||
|
|
||||||
# Do doctest if we're run directly
|
# Do doctest if we're run directly
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import doctest
|
import doctest
|
||||||
|
|
||||||
doctest.testmod()
|
doctest.testmod()
|
||||||
|
|
||||||
__all__ = ["newkeys", "encrypt", "decrypt", "sign", "verify", 'PublicKey',
|
__all__ = ["newkeys", "encrypt", "decrypt", "sign", "verify", 'PublicKey',
|
||||||
'PrivateKey', 'DecryptionError', 'VerificationError']
|
'PrivateKey', 'DecryptionError', 'VerificationError']
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
"""Python compatibility wrappers."""
|
"""Python compatibility wrappers."""
|
||||||
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
@ -42,15 +41,12 @@ else:
|
||||||
# Else we just assume 64-bit processor keeping up with modern times.
|
# Else we just assume 64-bit processor keeping up with modern times.
|
||||||
MACHINE_WORD_SIZE = 64
|
MACHINE_WORD_SIZE = 64
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# < Python3
|
# < Python3
|
||||||
unicode_type = unicode
|
unicode_type = unicode
|
||||||
have_python3 = False
|
|
||||||
except NameError:
|
except NameError:
|
||||||
# Python3.
|
# Python3.
|
||||||
unicode_type = str
|
unicode_type = str
|
||||||
have_python3 = True
|
|
||||||
|
|
||||||
# Fake byte literals.
|
# Fake byte literals.
|
||||||
if str is unicode_type:
|
if str is unicode_type:
|
||||||
|
@ -68,14 +64,6 @@ except NameError:
|
||||||
|
|
||||||
b = byte_literal
|
b = byte_literal
|
||||||
|
|
||||||
try:
|
|
||||||
# Python 2.6 or higher.
|
|
||||||
bytes_type = bytes
|
|
||||||
except NameError:
|
|
||||||
# Python 2.5
|
|
||||||
bytes_type = str
|
|
||||||
|
|
||||||
|
|
||||||
# To avoid calling b() multiple times in tight loops.
|
# To avoid calling b() multiple times in tight loops.
|
||||||
ZERO_BYTE = b('\x00')
|
ZERO_BYTE = b('\x00')
|
||||||
EMPTY_BYTE = b('')
|
EMPTY_BYTE = b('')
|
||||||
|
@ -90,7 +78,7 @@ def is_bytes(obj):
|
||||||
:returns:
|
:returns:
|
||||||
``True`` if ``value`` is a byte string; ``False`` otherwise.
|
``True`` if ``value`` is a byte string; ``False`` otherwise.
|
||||||
"""
|
"""
|
||||||
return isinstance(obj, bytes_type)
|
return isinstance(obj, bytes)
|
||||||
|
|
||||||
|
|
||||||
def is_integer(obj):
|
def is_integer(obj):
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -14,8 +14,11 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
"""RSA module
|
"""Deprecated version of the RSA module
|
||||||
pri = k[1] //Private part of keys d,p,q
|
|
||||||
|
.. deprecated:: 2.0
|
||||||
|
|
||||||
|
This submodule is deprecated and will be completely removed as of version 4.0.
|
||||||
|
|
||||||
Module for calculating large primes, and RSA encryption, decryption,
|
Module for calculating large primes, and RSA encryption, decryption,
|
||||||
signing and verification. Includes generating public and private keys.
|
signing and verification. Includes generating public and private keys.
|
||||||
|
@ -34,7 +37,11 @@ __version__ = '1.3.3'
|
||||||
# NOTE: Python's modulo can return negative numbers. We compensate for
|
# NOTE: Python's modulo can return negative numbers. We compensate for
|
||||||
# this behaviour using the abs() function
|
# this behaviour using the abs() function
|
||||||
|
|
||||||
from cPickle import dumps, loads
|
try:
|
||||||
|
import cPickle as pickle
|
||||||
|
except ImportError:
|
||||||
|
import pickle
|
||||||
|
from pickle import dumps, loads
|
||||||
import base64
|
import base64
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
|
@ -49,6 +56,9 @@ from rsa._compat import byte
|
||||||
import warnings
|
import warnings
|
||||||
warnings.warn('Insecure version of the RSA module is imported as %s, be careful'
|
warnings.warn('Insecure version of the RSA module is imported as %s, be careful'
|
||||||
% __name__)
|
% __name__)
|
||||||
|
warnings.warn('This submodule is deprecated and will be completely removed as of version 4.0.',
|
||||||
|
DeprecationWarning)
|
||||||
|
|
||||||
|
|
||||||
def gcd(p, q):
|
def gcd(p, q):
|
||||||
"""Returns the greatest common divisor of p and q
|
"""Returns the greatest common divisor of p and q
|
||||||
|
@ -63,12 +73,6 @@ def gcd(p, q):
|
||||||
|
|
||||||
def bytes2int(bytes):
|
def bytes2int(bytes):
|
||||||
"""Converts a list of bytes or a string to an integer
|
"""Converts a list of bytes or a string to an integer
|
||||||
|
|
||||||
>>> (128*256 + 64)*256 + + 15
|
|
||||||
8405007
|
|
||||||
>>> l = [128, 64, 15]
|
|
||||||
>>> bytes2int(l)
|
|
||||||
8405007
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not (type(bytes) is types.ListType or type(bytes) is types.StringType):
|
if not (type(bytes) is types.ListType or type(bytes) is types.StringType):
|
||||||
|
@ -85,9 +89,6 @@ def bytes2int(bytes):
|
||||||
|
|
||||||
def int2bytes(number):
|
def int2bytes(number):
|
||||||
"""Converts a number to a string of bytes
|
"""Converts a number to a string of bytes
|
||||||
|
|
||||||
>>> bytes2int(int2bytes(123456789))
|
|
||||||
123456789
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not (type(number) is types.LongType or type(number) is types.IntType):
|
if not (type(number) is types.LongType or type(number) is types.IntType):
|
||||||
|
@ -204,11 +205,6 @@ def randomized_primality_testing(n, k):
|
||||||
|
|
||||||
def is_prime(number):
|
def is_prime(number):
|
||||||
"""Returns True if the number is prime, and False otherwise.
|
"""Returns True if the number is prime, and False otherwise.
|
||||||
|
|
||||||
>>> is_prime(42)
|
|
||||||
0
|
|
||||||
>>> is_prime(41)
|
|
||||||
1
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -228,14 +224,6 @@ def is_prime(number):
|
||||||
def getprime(nbits):
|
def getprime(nbits):
|
||||||
"""Returns a prime number of max. 'math.ceil(nbits/8)*8' bits. In
|
"""Returns a prime number of max. 'math.ceil(nbits/8)*8' bits. In
|
||||||
other words: nbits is rounded up to whole bytes.
|
other words: nbits is rounded up to whole bytes.
|
||||||
|
|
||||||
>>> p = getprime(8)
|
|
||||||
>>> is_prime(p-1)
|
|
||||||
0
|
|
||||||
>>> is_prime(p)
|
|
||||||
1
|
|
||||||
>>> is_prime(p+1)
|
|
||||||
0
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
nbytes = int(math.ceil(nbits/8.))
|
nbytes = int(math.ceil(nbits/8.))
|
||||||
|
@ -256,11 +244,6 @@ def getprime(nbits):
|
||||||
def are_relatively_prime(a, b):
|
def are_relatively_prime(a, b):
|
||||||
"""Returns True if a and b are relatively prime, and False if they
|
"""Returns True if a and b are relatively prime, and False if they
|
||||||
are not.
|
are not.
|
||||||
|
|
||||||
>>> are_relatively_prime(2, 3)
|
|
||||||
1
|
|
||||||
>>> are_relatively_prime(2, 4)
|
|
||||||
0
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
d = gcd(a, b)
|
d = gcd(a, b)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -14,14 +14,11 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
"""RSA module
|
"""Deprecated version of the RSA module
|
||||||
|
|
||||||
Module for calculating large primes, and RSA encryption, decryption,
|
.. deprecated:: 3.0
|
||||||
signing and verification. Includes generating public and private keys.
|
|
||||||
|
|
||||||
WARNING: this implementation does not use random padding, compression of the
|
This submodule is deprecated and will be completely removed as of version 4.0.
|
||||||
cleartext input to prevent repetitions, or other common security improvements.
|
|
||||||
Use with care.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -39,6 +36,8 @@ from rsa._compat import byte
|
||||||
# Display a warning that this insecure version is imported.
|
# Display a warning that this insecure version is imported.
|
||||||
import warnings
|
import warnings
|
||||||
warnings.warn('Insecure version of the RSA module is imported as %s' % __name__)
|
warnings.warn('Insecure version of the RSA module is imported as %s' % __name__)
|
||||||
|
warnings.warn('This submodule is deprecated and will be completely removed as of version 4.0.',
|
||||||
|
DeprecationWarning)
|
||||||
|
|
||||||
|
|
||||||
def bit_size(number):
|
def bit_size(number):
|
||||||
|
@ -59,13 +58,7 @@ def gcd(p, q):
|
||||||
|
|
||||||
|
|
||||||
def bytes2int(bytes):
|
def bytes2int(bytes):
|
||||||
"""Converts a list of bytes or a string to an integer
|
r"""Converts a list of bytes or a string to an integer
|
||||||
|
|
||||||
>>> (((128 * 256) + 64) * 256) + 15
|
|
||||||
8405007
|
|
||||||
>>> l = [128, 64, 15]
|
|
||||||
>>> bytes2int(l) #same as bytes2int('\x80@\x0f')
|
|
||||||
8405007
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not (type(bytes) is types.ListType or type(bytes) is types.StringType):
|
if not (type(bytes) is types.ListType or type(bytes) is types.StringType):
|
||||||
|
@ -99,9 +92,6 @@ def int2bytes(number):
|
||||||
def to64(number):
|
def to64(number):
|
||||||
"""Converts a number in the range of 0 to 63 into base 64 digit
|
"""Converts a number in the range of 0 to 63 into base 64 digit
|
||||||
character in the range of '0'-'9', 'A'-'Z', 'a'-'z','-','_'.
|
character in the range of '0'-'9', 'A'-'Z', 'a'-'z','-','_'.
|
||||||
|
|
||||||
>>> to64(10)
|
|
||||||
'A'
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not (type(number) is types.LongType or type(number) is types.IntType):
|
if not (type(number) is types.LongType or type(number) is types.IntType):
|
||||||
|
@ -128,9 +118,6 @@ def to64(number):
|
||||||
def from64(number):
|
def from64(number):
|
||||||
"""Converts an ordinal character value in the range of
|
"""Converts an ordinal character value in the range of
|
||||||
0-9,A-Z,a-z,-,_ to a number in the range of 0-63.
|
0-9,A-Z,a-z,-,_ to a number in the range of 0-63.
|
||||||
|
|
||||||
>>> from64(49)
|
|
||||||
1
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not (type(number) is types.LongType or type(number) is types.IntType):
|
if not (type(number) is types.LongType or type(number) is types.IntType):
|
||||||
|
@ -157,9 +144,6 @@ def from64(number):
|
||||||
def int2str64(number):
|
def int2str64(number):
|
||||||
"""Converts a number to a string of base64 encoded characters in
|
"""Converts a number to a string of base64 encoded characters in
|
||||||
the range of '0'-'9','A'-'Z,'a'-'z','-','_'.
|
the range of '0'-'9','A'-'Z,'a'-'z','-','_'.
|
||||||
|
|
||||||
>>> int2str64(123456789)
|
|
||||||
'7MyqL'
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not (type(number) is types.LongType or type(number) is types.IntType):
|
if not (type(number) is types.LongType or type(number) is types.IntType):
|
||||||
|
@ -177,9 +161,6 @@ def int2str64(number):
|
||||||
def str642int(string):
|
def str642int(string):
|
||||||
"""Converts a base64 encoded string into an integer.
|
"""Converts a base64 encoded string into an integer.
|
||||||
The chars of this string in in the range '0'-'9','A'-'Z','a'-'z','-','_'
|
The chars of this string in in the range '0'-'9','A'-'Z','a'-'z','-','_'
|
||||||
|
|
||||||
>>> str642int('7MyqL')
|
|
||||||
123456789
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not (type(string) is types.ListType or type(string) is types.StringType):
|
if not (type(string) is types.ListType or type(string) is types.StringType):
|
||||||
|
@ -270,11 +251,6 @@ def randomized_primality_testing(n, k):
|
||||||
|
|
||||||
def is_prime(number):
|
def is_prime(number):
|
||||||
"""Returns True if the number is prime, and False otherwise.
|
"""Returns True if the number is prime, and False otherwise.
|
||||||
|
|
||||||
>>> is_prime(42)
|
|
||||||
0
|
|
||||||
>>> is_prime(41)
|
|
||||||
1
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if randomized_primality_testing(number, 6):
|
if randomized_primality_testing(number, 6):
|
||||||
|
@ -288,14 +264,6 @@ def is_prime(number):
|
||||||
def getprime(nbits):
|
def getprime(nbits):
|
||||||
"""Returns a prime number of max. 'math.ceil(nbits/8)*8' bits. In
|
"""Returns a prime number of max. 'math.ceil(nbits/8)*8' bits. In
|
||||||
other words: nbits is rounded up to whole bytes.
|
other words: nbits is rounded up to whole bytes.
|
||||||
|
|
||||||
>>> p = getprime(8)
|
|
||||||
>>> is_prime(p-1)
|
|
||||||
0
|
|
||||||
>>> is_prime(p)
|
|
||||||
1
|
|
||||||
>>> is_prime(p+1)
|
|
||||||
0
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -14,19 +14,21 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
'''ASN.1 definitions.
|
"""ASN.1 definitions.
|
||||||
|
|
||||||
Not all ASN.1-handling code use these definitions, but when it does, they should be here.
|
Not all ASN.1-handling code use these definitions, but when it does, they should be here.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
from pyasn1.type import univ, namedtype, tag
|
from pyasn1.type import univ, namedtype, tag
|
||||||
|
|
||||||
|
|
||||||
class PubKeyHeader(univ.Sequence):
|
class PubKeyHeader(univ.Sequence):
|
||||||
componentType = namedtype.NamedTypes(
|
componentType = namedtype.NamedTypes(
|
||||||
namedtype.NamedType('oid', univ.ObjectIdentifier()),
|
namedtype.NamedType('oid', univ.ObjectIdentifier()),
|
||||||
namedtype.NamedType('parameters', univ.Null()),
|
namedtype.NamedType('parameters', univ.Null()),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class OpenSSLPubKey(univ.Sequence):
|
class OpenSSLPubKey(univ.Sequence):
|
||||||
componentType = namedtype.NamedTypes(
|
componentType = namedtype.NamedTypes(
|
||||||
namedtype.NamedType('header', PubKeyHeader()),
|
namedtype.NamedType('header', PubKeyHeader()),
|
||||||
|
@ -38,12 +40,12 @@ class OpenSSLPubKey(univ.Sequence):
|
||||||
|
|
||||||
|
|
||||||
class AsnPubKey(univ.Sequence):
|
class AsnPubKey(univ.Sequence):
|
||||||
'''ASN.1 contents of DER encoded public key:
|
"""ASN.1 contents of DER encoded public key:
|
||||||
|
|
||||||
RSAPublicKey ::= SEQUENCE {
|
RSAPublicKey ::= SEQUENCE {
|
||||||
modulus INTEGER, -- n
|
modulus INTEGER, -- n
|
||||||
publicExponent INTEGER, -- e
|
publicExponent INTEGER, -- e
|
||||||
'''
|
"""
|
||||||
|
|
||||||
componentType = namedtype.NamedTypes(
|
componentType = namedtype.NamedTypes(
|
||||||
namedtype.NamedType('modulus', univ.Integer()),
|
namedtype.NamedType('modulus', univ.Integer()),
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -14,7 +14,27 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
'''Large file support
|
"""Large file support
|
||||||
|
|
||||||
|
.. deprecated:: 3.4
|
||||||
|
|
||||||
|
The VARBLOCK format is NOT recommended for general use, has been deprecated since
|
||||||
|
Python-RSA 3.4, and will be removed in a future release. It's vulnerable to a
|
||||||
|
number of attacks:
|
||||||
|
|
||||||
|
1. decrypt/encrypt_bigfile() does not implement `Authenticated encryption`_ nor
|
||||||
|
uses MACs to verify messages before decrypting public key encrypted messages.
|
||||||
|
|
||||||
|
2. decrypt/encrypt_bigfile() does not use hybrid encryption (it uses plain RSA)
|
||||||
|
and has no method for chaining, so block reordering is possible.
|
||||||
|
|
||||||
|
See `issue #19 on Github`_ for more information.
|
||||||
|
|
||||||
|
.. _Authenticated encryption: https://en.wikipedia.org/wiki/Authenticated_encryption
|
||||||
|
.. _issue #19 on Github: https://github.com/sybrenstuvel/python-rsa/issues/13
|
||||||
|
|
||||||
|
|
||||||
|
This module contains functions to:
|
||||||
|
|
||||||
- break a file into smaller blocks, and encrypt them, and store the
|
- break a file into smaller blocks, and encrypt them, and store the
|
||||||
encrypted blocks in another file.
|
encrypted blocks in another file.
|
||||||
|
@ -37,19 +57,34 @@ The encrypted file format is as follows, where || denotes byte concatenation:
|
||||||
This file format is called the VARBLOCK format, in line with the varint format
|
This file format is called the VARBLOCK format, in line with the varint format
|
||||||
used to denote the block sizes.
|
used to denote the block sizes.
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
import warnings
|
||||||
|
|
||||||
from rsa import key, common, pkcs1, varblock
|
from rsa import key, common, pkcs1, varblock
|
||||||
from rsa._compat import byte
|
from rsa._compat import byte
|
||||||
|
|
||||||
|
|
||||||
def encrypt_bigfile(infile, outfile, pub_key):
|
def encrypt_bigfile(infile, outfile, pub_key):
|
||||||
'''Encrypts a file, writing it to 'outfile' in VARBLOCK format.
|
"""Encrypts a file, writing it to 'outfile' in VARBLOCK format.
|
||||||
|
|
||||||
|
.. deprecated:: 3.4
|
||||||
|
This function was deprecated in Python-RSA version 3.4 due to security issues
|
||||||
|
in the VARBLOCK format. See the documentation_ for more information.
|
||||||
|
|
||||||
|
.. _documentation: https://stuvel.eu/python-rsa-doc/usage.html#working-with-big-files
|
||||||
|
|
||||||
:param infile: file-like object to read the cleartext from
|
:param infile: file-like object to read the cleartext from
|
||||||
:param outfile: file-like object to write the crypto in VARBLOCK format to
|
:param outfile: file-like object to write the crypto in VARBLOCK format to
|
||||||
:param pub_key: :py:class:`rsa.PublicKey` to encrypt with
|
:param pub_key: :py:class:`rsa.PublicKey` to encrypt with
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
warnings.warn("The 'rsa.bigfile.encrypt_bigfile' function was deprecated in Python-RSA version "
|
||||||
|
"3.4 due to security issues in the VARBLOCK format. See "
|
||||||
|
"https://stuvel.eu/python-rsa-doc/usage.html#working-with-big-files "
|
||||||
|
"for more information.",
|
||||||
|
DeprecationWarning, stacklevel=2)
|
||||||
|
|
||||||
if not isinstance(pub_key, key.PublicKey):
|
if not isinstance(pub_key, key.PublicKey):
|
||||||
raise TypeError('Public key required, but got %r' % pub_key)
|
raise TypeError('Public key required, but got %r' % pub_key)
|
||||||
|
@ -67,14 +102,27 @@ def encrypt_bigfile(infile, outfile, pub_key):
|
||||||
varblock.write_varint(outfile, len(crypto))
|
varblock.write_varint(outfile, len(crypto))
|
||||||
outfile.write(crypto)
|
outfile.write(crypto)
|
||||||
|
|
||||||
|
|
||||||
def decrypt_bigfile(infile, outfile, priv_key):
|
def decrypt_bigfile(infile, outfile, priv_key):
|
||||||
'''Decrypts an encrypted VARBLOCK file, writing it to 'outfile'
|
"""Decrypts an encrypted VARBLOCK file, writing it to 'outfile'
|
||||||
|
|
||||||
|
.. deprecated:: 3.4
|
||||||
|
This function was deprecated in Python-RSA version 3.4 due to security issues
|
||||||
|
in the VARBLOCK format. See the documentation_ for more information.
|
||||||
|
|
||||||
|
.. _documentation: https://stuvel.eu/python-rsa-doc/usage.html#working-with-big-files
|
||||||
|
|
||||||
:param infile: file-like object to read the crypto in VARBLOCK format from
|
:param infile: file-like object to read the crypto in VARBLOCK format from
|
||||||
:param outfile: file-like object to write the cleartext to
|
:param outfile: file-like object to write the cleartext to
|
||||||
:param priv_key: :py:class:`rsa.PrivateKey` to decrypt with
|
:param priv_key: :py:class:`rsa.PrivateKey` to decrypt with
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
warnings.warn("The 'rsa.bigfile.decrypt_bigfile' function was deprecated in Python-RSA version "
|
||||||
|
"3.4 due to security issues in the VARBLOCK format. See "
|
||||||
|
"https://stuvel.eu/python-rsa-doc/usage.html#working-with-big-files "
|
||||||
|
"for more information.",
|
||||||
|
DeprecationWarning, stacklevel=2)
|
||||||
|
|
||||||
if not isinstance(priv_key, key.PrivateKey):
|
if not isinstance(priv_key, key.PrivateKey):
|
||||||
raise TypeError('Private key required, but got %r' % priv_key)
|
raise TypeError('Private key required, but got %r' % priv_key)
|
||||||
|
@ -83,5 +131,5 @@ def decrypt_bigfile(infile, outfile, priv_key):
|
||||||
cleartext = pkcs1.decrypt(block, priv_key)
|
cleartext = pkcs1.decrypt(block, priv_key)
|
||||||
outfile.write(cleartext)
|
outfile.write(cleartext)
|
||||||
|
|
||||||
__all__ = ['encrypt_bigfile', 'decrypt_bigfile']
|
|
||||||
|
|
||||||
|
__all__ = ['encrypt_bigfile', 'decrypt_bigfile']
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -14,10 +14,10 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
'''Commandline scripts.
|
"""Commandline scripts.
|
||||||
|
|
||||||
These scripts are called by the executables defined in setup.py.
|
These scripts are called by the executables defined in setup.py.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
from __future__ import with_statement, print_function
|
from __future__ import with_statement, print_function
|
||||||
|
|
||||||
|
@ -31,8 +31,9 @@ import rsa.pkcs1
|
||||||
|
|
||||||
HASH_METHODS = sorted(rsa.pkcs1.HASH_METHODS.keys())
|
HASH_METHODS = sorted(rsa.pkcs1.HASH_METHODS.keys())
|
||||||
|
|
||||||
|
|
||||||
def keygen():
|
def keygen():
|
||||||
'''Key generator.'''
|
"""Key generator."""
|
||||||
|
|
||||||
# Parse the CLI options
|
# Parse the CLI options
|
||||||
parser = OptionParser(usage='usage: %prog [options] keysize',
|
parser = OptionParser(usage='usage: %prog [options] keysize',
|
||||||
|
@ -67,7 +68,6 @@ def keygen():
|
||||||
print('Generating %i-bit key' % keysize, file=sys.stderr)
|
print('Generating %i-bit key' % keysize, file=sys.stderr)
|
||||||
(pub_key, priv_key) = rsa.newkeys(keysize)
|
(pub_key, priv_key) = rsa.newkeys(keysize)
|
||||||
|
|
||||||
|
|
||||||
# Save public key
|
# Save public key
|
||||||
if cli.pubout:
|
if cli.pubout:
|
||||||
print('Writing public key to %s' % cli.pubout, file=sys.stderr)
|
print('Writing public key to %s' % cli.pubout, file=sys.stderr)
|
||||||
|
@ -88,7 +88,7 @@ def keygen():
|
||||||
|
|
||||||
|
|
||||||
class CryptoOperation(object):
|
class CryptoOperation(object):
|
||||||
'''CLI callable that operates with input, output, and a key.'''
|
"""CLI callable that operates with input, output, and a key."""
|
||||||
|
|
||||||
__metaclass__ = abc.ABCMeta
|
__metaclass__ = abc.ABCMeta
|
||||||
|
|
||||||
|
@ -114,15 +114,15 @@ class CryptoOperation(object):
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def perform_operation(self, indata, key, cli_args=None):
|
def perform_operation(self, indata, key, cli_args=None):
|
||||||
'''Performs the program's operation.
|
"""Performs the program's operation.
|
||||||
|
|
||||||
Implement in a subclass.
|
Implement in a subclass.
|
||||||
|
|
||||||
:returns: the data to write to the output.
|
:returns: the data to write to the output.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
'''Runs the program.'''
|
"""Runs the program."""
|
||||||
|
|
||||||
(cli, cli_args) = self.parse_cli()
|
(cli, cli_args) = self.parse_cli()
|
||||||
|
|
||||||
|
@ -137,10 +137,10 @@ class CryptoOperation(object):
|
||||||
self.write_outfile(outdata, cli.output)
|
self.write_outfile(outdata, cli.output)
|
||||||
|
|
||||||
def parse_cli(self):
|
def parse_cli(self):
|
||||||
'''Parse the CLI options
|
"""Parse the CLI options
|
||||||
|
|
||||||
:returns: (cli_opts, cli_args)
|
:returns: (cli_opts, cli_args)
|
||||||
'''
|
"""
|
||||||
|
|
||||||
parser = OptionParser(usage=self.usage, description=self.description)
|
parser = OptionParser(usage=self.usage, description=self.description)
|
||||||
|
|
||||||
|
@ -159,10 +159,10 @@ class CryptoOperation(object):
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
|
||||||
return (cli, cli_args)
|
return cli, cli_args
|
||||||
|
|
||||||
def read_key(self, filename, keyform):
|
def read_key(self, filename, keyform):
|
||||||
'''Reads a public or private key.'''
|
"""Reads a public or private key."""
|
||||||
|
|
||||||
print('Reading %s key from %s' % (self.keyname, filename), file=sys.stderr)
|
print('Reading %s key from %s' % (self.keyname, filename), file=sys.stderr)
|
||||||
with open(filename, 'rb') as keyfile:
|
with open(filename, 'rb') as keyfile:
|
||||||
|
@ -171,7 +171,7 @@ class CryptoOperation(object):
|
||||||
return self.key_class.load_pkcs1(keydata, keyform)
|
return self.key_class.load_pkcs1(keydata, keyform)
|
||||||
|
|
||||||
def read_infile(self, inname):
|
def read_infile(self, inname):
|
||||||
'''Read the input file'''
|
"""Read the input file"""
|
||||||
|
|
||||||
if inname:
|
if inname:
|
||||||
print('Reading input from %s' % inname, file=sys.stderr)
|
print('Reading input from %s' % inname, file=sys.stderr)
|
||||||
|
@ -182,7 +182,7 @@ class CryptoOperation(object):
|
||||||
return sys.stdin.read()
|
return sys.stdin.read()
|
||||||
|
|
||||||
def write_outfile(self, outdata, outname):
|
def write_outfile(self, outdata, outname):
|
||||||
'''Write the output file'''
|
"""Write the output file"""
|
||||||
|
|
||||||
if outname:
|
if outname:
|
||||||
print('Writing output to %s' % outname, file=sys.stderr)
|
print('Writing output to %s' % outname, file=sys.stderr)
|
||||||
|
@ -192,8 +192,9 @@ class CryptoOperation(object):
|
||||||
print('Writing output to stdout', file=sys.stderr)
|
print('Writing output to stdout', file=sys.stderr)
|
||||||
sys.stdout.write(outdata)
|
sys.stdout.write(outdata)
|
||||||
|
|
||||||
|
|
||||||
class EncryptOperation(CryptoOperation):
|
class EncryptOperation(CryptoOperation):
|
||||||
'''Encrypts a file.'''
|
"""Encrypts a file."""
|
||||||
|
|
||||||
keyname = 'public'
|
keyname = 'public'
|
||||||
description = ('Encrypts a file. The file must be shorter than the key '
|
description = ('Encrypts a file. The file must be shorter than the key '
|
||||||
|
@ -203,14 +204,14 @@ class EncryptOperation(CryptoOperation):
|
||||||
operation_past = 'encrypted'
|
operation_past = 'encrypted'
|
||||||
operation_progressive = 'encrypting'
|
operation_progressive = 'encrypting'
|
||||||
|
|
||||||
|
|
||||||
def perform_operation(self, indata, pub_key, cli_args=None):
|
def perform_operation(self, indata, pub_key, cli_args=None):
|
||||||
'''Encrypts files.'''
|
"""Encrypts files."""
|
||||||
|
|
||||||
return rsa.encrypt(indata, pub_key)
|
return rsa.encrypt(indata, pub_key)
|
||||||
|
|
||||||
|
|
||||||
class DecryptOperation(CryptoOperation):
|
class DecryptOperation(CryptoOperation):
|
||||||
'''Decrypts a file.'''
|
"""Decrypts a file."""
|
||||||
|
|
||||||
keyname = 'private'
|
keyname = 'private'
|
||||||
description = ('Decrypts a file. The original file must be shorter than '
|
description = ('Decrypts a file. The original file must be shorter than '
|
||||||
|
@ -222,12 +223,13 @@ class DecryptOperation(CryptoOperation):
|
||||||
key_class = rsa.PrivateKey
|
key_class = rsa.PrivateKey
|
||||||
|
|
||||||
def perform_operation(self, indata, priv_key, cli_args=None):
|
def perform_operation(self, indata, priv_key, cli_args=None):
|
||||||
'''Decrypts files.'''
|
"""Decrypts files."""
|
||||||
|
|
||||||
return rsa.decrypt(indata, priv_key)
|
return rsa.decrypt(indata, priv_key)
|
||||||
|
|
||||||
|
|
||||||
class SignOperation(CryptoOperation):
|
class SignOperation(CryptoOperation):
|
||||||
'''Signs a file.'''
|
"""Signs a file."""
|
||||||
|
|
||||||
keyname = 'private'
|
keyname = 'private'
|
||||||
usage = 'usage: %%prog [options] private_key hash_method'
|
usage = 'usage: %%prog [options] private_key hash_method'
|
||||||
|
@ -243,7 +245,7 @@ class SignOperation(CryptoOperation):
|
||||||
'to stdout if this option is not present.')
|
'to stdout if this option is not present.')
|
||||||
|
|
||||||
def perform_operation(self, indata, priv_key, cli_args):
|
def perform_operation(self, indata, priv_key, cli_args):
|
||||||
'''Decrypts files.'''
|
"""Signs files."""
|
||||||
|
|
||||||
hash_method = cli_args[1]
|
hash_method = cli_args[1]
|
||||||
if hash_method not in HASH_METHODS:
|
if hash_method not in HASH_METHODS:
|
||||||
|
@ -252,8 +254,9 @@ class SignOperation(CryptoOperation):
|
||||||
|
|
||||||
return rsa.sign(indata, priv_key, hash_method)
|
return rsa.sign(indata, priv_key, hash_method)
|
||||||
|
|
||||||
|
|
||||||
class VerifyOperation(CryptoOperation):
|
class VerifyOperation(CryptoOperation):
|
||||||
'''Verify a signature.'''
|
"""Verify a signature."""
|
||||||
|
|
||||||
keyname = 'public'
|
keyname = 'public'
|
||||||
usage = 'usage: %%prog [options] public_key signature_file'
|
usage = 'usage: %%prog [options] public_key signature_file'
|
||||||
|
@ -267,7 +270,7 @@ class VerifyOperation(CryptoOperation):
|
||||||
has_output = False
|
has_output = False
|
||||||
|
|
||||||
def perform_operation(self, indata, pub_key, cli_args):
|
def perform_operation(self, indata, pub_key, cli_args):
|
||||||
'''Decrypts files.'''
|
"""Verifies files."""
|
||||||
|
|
||||||
signature_file = cli_args[1]
|
signature_file = cli_args[1]
|
||||||
|
|
||||||
|
@ -283,7 +286,7 @@ class VerifyOperation(CryptoOperation):
|
||||||
|
|
||||||
|
|
||||||
class BigfileOperation(CryptoOperation):
|
class BigfileOperation(CryptoOperation):
|
||||||
'''CryptoOperation that doesn't read the entire file into memory.'''
|
"""CryptoOperation that doesn't read the entire file into memory."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
CryptoOperation.__init__(self)
|
CryptoOperation.__init__(self)
|
||||||
|
@ -291,13 +294,13 @@ class BigfileOperation(CryptoOperation):
|
||||||
self.file_objects = []
|
self.file_objects = []
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
'''Closes any open file handles.'''
|
"""Closes any open file handles."""
|
||||||
|
|
||||||
for fobj in self.file_objects:
|
for fobj in self.file_objects:
|
||||||
fobj.close()
|
fobj.close()
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
'''Runs the program.'''
|
"""Runs the program."""
|
||||||
|
|
||||||
(cli, cli_args) = self.parse_cli()
|
(cli, cli_args) = self.parse_cli()
|
||||||
|
|
||||||
|
@ -312,7 +315,7 @@ class BigfileOperation(CryptoOperation):
|
||||||
self.perform_operation(infile, outfile, key, cli_args)
|
self.perform_operation(infile, outfile, key, cli_args)
|
||||||
|
|
||||||
def get_infile(self, inname):
|
def get_infile(self, inname):
|
||||||
'''Returns the input file object'''
|
"""Returns the input file object"""
|
||||||
|
|
||||||
if inname:
|
if inname:
|
||||||
print('Reading input from %s' % inname, file=sys.stderr)
|
print('Reading input from %s' % inname, file=sys.stderr)
|
||||||
|
@ -325,7 +328,7 @@ class BigfileOperation(CryptoOperation):
|
||||||
return fobj
|
return fobj
|
||||||
|
|
||||||
def get_outfile(self, outname):
|
def get_outfile(self, outname):
|
||||||
'''Returns the output file object'''
|
"""Returns the output file object"""
|
||||||
|
|
||||||
if outname:
|
if outname:
|
||||||
print('Will write output to %s' % outname, file=sys.stderr)
|
print('Will write output to %s' % outname, file=sys.stderr)
|
||||||
|
@ -337,8 +340,9 @@ class BigfileOperation(CryptoOperation):
|
||||||
|
|
||||||
return fobj
|
return fobj
|
||||||
|
|
||||||
|
|
||||||
class EncryptBigfileOperation(BigfileOperation):
|
class EncryptBigfileOperation(BigfileOperation):
|
||||||
'''Encrypts a file to VARBLOCK format.'''
|
"""Encrypts a file to VARBLOCK format."""
|
||||||
|
|
||||||
keyname = 'public'
|
keyname = 'public'
|
||||||
description = ('Encrypts a file to an encrypted VARBLOCK file. The file '
|
description = ('Encrypts a file to an encrypted VARBLOCK file. The file '
|
||||||
|
@ -349,12 +353,13 @@ class EncryptBigfileOperation(BigfileOperation):
|
||||||
operation_progressive = 'encrypting'
|
operation_progressive = 'encrypting'
|
||||||
|
|
||||||
def perform_operation(self, infile, outfile, pub_key, cli_args=None):
|
def perform_operation(self, infile, outfile, pub_key, cli_args=None):
|
||||||
'''Encrypts files to VARBLOCK.'''
|
"""Encrypts files to VARBLOCK."""
|
||||||
|
|
||||||
return rsa.bigfile.encrypt_bigfile(infile, outfile, pub_key)
|
return rsa.bigfile.encrypt_bigfile(infile, outfile, pub_key)
|
||||||
|
|
||||||
|
|
||||||
class DecryptBigfileOperation(BigfileOperation):
|
class DecryptBigfileOperation(BigfileOperation):
|
||||||
'''Decrypts a file in VARBLOCK format.'''
|
"""Decrypts a file in VARBLOCK format."""
|
||||||
|
|
||||||
keyname = 'private'
|
keyname = 'private'
|
||||||
description = ('Decrypts an encrypted VARBLOCK file that was encrypted '
|
description = ('Decrypts an encrypted VARBLOCK file that was encrypted '
|
||||||
|
@ -365,7 +370,7 @@ class DecryptBigfileOperation(BigfileOperation):
|
||||||
key_class = rsa.PrivateKey
|
key_class = rsa.PrivateKey
|
||||||
|
|
||||||
def perform_operation(self, infile, outfile, priv_key, cli_args=None):
|
def perform_operation(self, infile, outfile, priv_key, cli_args=None):
|
||||||
'''Decrypts a VARBLOCK file.'''
|
"""Decrypts a VARBLOCK file."""
|
||||||
|
|
||||||
return rsa.bigfile.decrypt_bigfile(infile, outfile, priv_key)
|
return rsa.bigfile.decrypt_bigfile(infile, outfile, priv_key)
|
||||||
|
|
||||||
|
@ -376,4 +381,3 @@ sign = SignOperation()
|
||||||
verify = VerifyOperation()
|
verify = VerifyOperation()
|
||||||
encrypt_bigfile = EncryptBigfileOperation()
|
encrypt_bigfile = EncryptBigfileOperation()
|
||||||
decrypt_bigfile = DecryptBigfileOperation()
|
decrypt_bigfile = DecryptBigfileOperation()
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -14,15 +14,15 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
'''Common functionality shared by several modules.'''
|
"""Common functionality shared by several modules."""
|
||||||
|
|
||||||
|
|
||||||
def bit_size(num):
|
def bit_size(num):
|
||||||
'''
|
"""
|
||||||
Number of bits needed to represent a integer excluding any prefix
|
Number of bits needed to represent a integer excluding any prefix
|
||||||
0 bits.
|
0 bits.
|
||||||
|
|
||||||
As per definition from http://wiki.python.org/moin/BitManipulation and
|
As per definition from https://wiki.python.org/moin/BitManipulation and
|
||||||
to match the behavior of the Python 3 API.
|
to match the behavior of the Python 3 API.
|
||||||
|
|
||||||
Usage::
|
Usage::
|
||||||
|
@ -40,7 +40,7 @@ def bit_size(num):
|
||||||
before the number's bit length is determined.
|
before the number's bit length is determined.
|
||||||
:returns:
|
:returns:
|
||||||
Returns the number of bits in the integer.
|
Returns the number of bits in the integer.
|
||||||
'''
|
"""
|
||||||
if num == 0:
|
if num == 0:
|
||||||
return 0
|
return 0
|
||||||
if num < 0:
|
if num < 0:
|
||||||
|
@ -51,17 +51,17 @@ def bit_size(num):
|
||||||
|
|
||||||
hex_num = "%x" % num
|
hex_num = "%x" % num
|
||||||
return ((len(hex_num) - 1) * 4) + {
|
return ((len(hex_num) - 1) * 4) + {
|
||||||
'0':0, '1':1, '2':2, '3':2,
|
'0': 0, '1': 1, '2': 2, '3': 2,
|
||||||
'4':3, '5':3, '6':3, '7':3,
|
'4': 3, '5': 3, '6': 3, '7': 3,
|
||||||
'8':4, '9':4, 'a':4, 'b':4,
|
'8': 4, '9': 4, 'a': 4, 'b': 4,
|
||||||
'c':4, 'd':4, 'e':4, 'f':4,
|
'c': 4, 'd': 4, 'e': 4, 'f': 4,
|
||||||
}[hex_num[0]]
|
}[hex_num[0]]
|
||||||
|
|
||||||
|
|
||||||
def _bit_size(number):
|
def _bit_size(number):
|
||||||
'''
|
"""
|
||||||
Returns the number of bits required to hold a specific long number.
|
Returns the number of bits required to hold a specific long number.
|
||||||
'''
|
"""
|
||||||
if number < 0:
|
if number < 0:
|
||||||
raise ValueError('Only nonnegative numbers possible: %s' % number)
|
raise ValueError('Only nonnegative numbers possible: %s' % number)
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ def _bit_size(number):
|
||||||
|
|
||||||
|
|
||||||
def byte_size(number):
|
def byte_size(number):
|
||||||
'''
|
"""
|
||||||
Returns the number of bytes required to hold a specific long number.
|
Returns the number of bytes required to hold a specific long number.
|
||||||
|
|
||||||
The number of bytes is rounded up.
|
The number of bytes is rounded up.
|
||||||
|
@ -97,17 +97,17 @@ def byte_size(number):
|
||||||
An unsigned integer
|
An unsigned integer
|
||||||
:returns:
|
:returns:
|
||||||
The number of bytes required to hold a specific long number.
|
The number of bytes required to hold a specific long number.
|
||||||
'''
|
"""
|
||||||
quanta, mod = divmod(bit_size(number), 8)
|
quanta, mod = divmod(bit_size(number), 8)
|
||||||
if mod or number == 0:
|
if mod or number == 0:
|
||||||
quanta += 1
|
quanta += 1
|
||||||
return quanta
|
return quanta
|
||||||
#return int(math.ceil(bit_size(number) / 8.0))
|
# return int(math.ceil(bit_size(number) / 8.0))
|
||||||
|
|
||||||
|
|
||||||
def extended_gcd(a, b):
|
def extended_gcd(a, b):
|
||||||
'''Returns a tuple (r, i, j) such that r = gcd(a, b) = ia + jb
|
"""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
|
# r = gcd(a,b) i = multiplicitive inverse of a mod b
|
||||||
# or j = multiplicitive inverse of b mod a
|
# or j = multiplicitive inverse of b mod a
|
||||||
# Neg return values for i or j are made positive mod b or a respectively
|
# Neg return values for i or j are made positive mod b or a respectively
|
||||||
|
@ -116,26 +116,28 @@ def extended_gcd(a, b):
|
||||||
y = 1
|
y = 1
|
||||||
lx = 1
|
lx = 1
|
||||||
ly = 0
|
ly = 0
|
||||||
oa = a #Remember original a/b to remove
|
oa = a # Remember original a/b to remove
|
||||||
ob = b #negative values from return results
|
ob = b # negative values from return results
|
||||||
while b != 0:
|
while b != 0:
|
||||||
q = a // b
|
q = a // b
|
||||||
(a, b) = (b, a % b)
|
(a, b) = (b, a % b)
|
||||||
(x, lx) = ((lx - (q * x)),x)
|
(x, lx) = ((lx - (q * x)), x)
|
||||||
(y, ly) = ((ly - (q * y)),y)
|
(y, ly) = ((ly - (q * y)), y)
|
||||||
if (lx < 0): lx += ob #If neg wrap modulo orignal b
|
if lx < 0:
|
||||||
if (ly < 0): ly += oa #If neg wrap modulo orignal a
|
lx += ob # If neg wrap modulo orignal b
|
||||||
return (a, lx, ly) #Return only positive values
|
if ly < 0:
|
||||||
|
ly += oa # If neg wrap modulo orignal a
|
||||||
|
return a, lx, ly # Return only positive values
|
||||||
|
|
||||||
|
|
||||||
def inverse(x, n):
|
def inverse(x, n):
|
||||||
'''Returns x^-1 (mod n)
|
"""Returns x^-1 (mod n)
|
||||||
|
|
||||||
>>> inverse(7, 4)
|
>>> inverse(7, 4)
|
||||||
3
|
3
|
||||||
>>> (inverse(143, 4) * 143) % 4
|
>>> (inverse(143, 4) * 143) % 4
|
||||||
1
|
1
|
||||||
'''
|
"""
|
||||||
|
|
||||||
(divider, inv, _) = extended_gcd(x, n)
|
(divider, inv, _) = extended_gcd(x, n)
|
||||||
|
|
||||||
|
@ -146,7 +148,7 @@ def inverse(x, n):
|
||||||
|
|
||||||
|
|
||||||
def crt(a_values, modulo_values):
|
def crt(a_values, modulo_values):
|
||||||
'''Chinese Remainder Theorem.
|
"""Chinese Remainder Theorem.
|
||||||
|
|
||||||
Calculates x such that x = a[i] (mod m[i]) for each i.
|
Calculates x such that x = a[i] (mod m[i]) for each i.
|
||||||
|
|
||||||
|
@ -163,7 +165,7 @@ def crt(a_values, modulo_values):
|
||||||
|
|
||||||
>>> crt([2, 3, 0], [7, 11, 15])
|
>>> crt([2, 3, 0], [7, 11, 15])
|
||||||
135
|
135
|
||||||
'''
|
"""
|
||||||
|
|
||||||
m = 1
|
m = 1
|
||||||
x = 0
|
x = 0
|
||||||
|
@ -179,7 +181,8 @@ def crt(a_values, modulo_values):
|
||||||
|
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import doctest
|
import doctest
|
||||||
doctest.testmod()
|
|
||||||
|
|
||||||
|
doctest.testmod()
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -14,24 +14,24 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
'''Core mathematical operations.
|
"""Core mathematical operations.
|
||||||
|
|
||||||
This is the actual core RSA implementation, which is only defined
|
This is the actual core RSA implementation, which is only defined
|
||||||
mathematically on integers.
|
mathematically on integers.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
|
||||||
from rsa._compat import is_integer
|
from rsa._compat import is_integer
|
||||||
|
|
||||||
def assert_int(var, name):
|
|
||||||
|
|
||||||
|
def assert_int(var, name):
|
||||||
if is_integer(var):
|
if is_integer(var):
|
||||||
return
|
return
|
||||||
|
|
||||||
raise TypeError('%s should be an integer, not %s' % (name, var.__class__))
|
raise TypeError('%s should be an integer, not %s' % (name, var.__class__))
|
||||||
|
|
||||||
|
|
||||||
def encrypt_int(message, ekey, n):
|
def encrypt_int(message, ekey, n):
|
||||||
'''Encrypts a message using encryption key 'ekey', working modulo n'''
|
"""Encrypts a message using encryption key 'ekey', working modulo n"""
|
||||||
|
|
||||||
assert_int(message, 'message')
|
assert_int(message, 'message')
|
||||||
assert_int(ekey, 'ekey')
|
assert_int(ekey, 'ekey')
|
||||||
|
@ -45,9 +45,9 @@ def encrypt_int(message, ekey, n):
|
||||||
|
|
||||||
return pow(message, ekey, n)
|
return pow(message, ekey, n)
|
||||||
|
|
||||||
|
|
||||||
def decrypt_int(cyphertext, dkey, n):
|
def decrypt_int(cyphertext, dkey, n):
|
||||||
'''Decrypts a cypher text using the decryption key 'dkey', working
|
"""Decrypts a cypher text using the decryption key 'dkey', working modulo n"""
|
||||||
modulo n'''
|
|
||||||
|
|
||||||
assert_int(cyphertext, 'cyphertext')
|
assert_int(cyphertext, 'cyphertext')
|
||||||
assert_int(dkey, 'dkey')
|
assert_int(dkey, 'dkey')
|
||||||
|
@ -55,4 +55,3 @@ def decrypt_int(cyphertext, dkey, n):
|
||||||
|
|
||||||
message = pow(cyphertext, dkey, n)
|
message = pow(cyphertext, dkey, n)
|
||||||
return message
|
return message
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
'''RSA key generation code.
|
"""RSA key generation code.
|
||||||
|
|
||||||
Create new keys with the newkeys() function. It will give you a PublicKey and a
|
Create new keys with the newkeys() function. It will give you a PublicKey and a
|
||||||
PrivateKey object.
|
PrivateKey object.
|
||||||
|
@ -23,70 +23,118 @@ 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
|
late as possible, such that other functionality will remain working in absence
|
||||||
of pyasn1.
|
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 logging
|
||||||
from rsa._compat import b, bytes_type
|
from rsa._compat import b
|
||||||
|
|
||||||
import rsa.prime
|
import rsa.prime
|
||||||
import rsa.pem
|
import rsa.pem
|
||||||
import rsa.common
|
import rsa.common
|
||||||
|
import rsa.randnum
|
||||||
|
import rsa.core
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
DEFAULT_EXPONENT = 65537
|
||||||
|
|
||||||
|
|
||||||
class AbstractKey(object):
|
class AbstractKey(object):
|
||||||
'''Abstract superclass for private and public keys.'''
|
"""Abstract superclass for private and public keys."""
|
||||||
|
|
||||||
|
__slots__ = ('n', 'e')
|
||||||
|
|
||||||
|
def __init__(self, n, e):
|
||||||
|
self.n = n
|
||||||
|
self.e = e
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_pkcs1(cls, keyfile, format='PEM'):
|
def load_pkcs1(cls, keyfile, format='PEM'):
|
||||||
r'''Loads a key in PKCS#1 DER or PEM format.
|
"""Loads a key in PKCS#1 DER or PEM format.
|
||||||
|
|
||||||
:param keyfile: contents of a DER- or PEM-encoded file that contains
|
:param keyfile: contents of a DER- or PEM-encoded file that contains
|
||||||
the public key.
|
the public key.
|
||||||
:param format: the format of the file to load; 'PEM' or 'DER'
|
:param format: the format of the file to load; 'PEM' or 'DER'
|
||||||
|
|
||||||
:return: a PublicKey object
|
:return: a PublicKey object
|
||||||
|
"""
|
||||||
'''
|
|
||||||
|
|
||||||
methods = {
|
methods = {
|
||||||
'PEM': cls._load_pkcs1_pem,
|
'PEM': cls._load_pkcs1_pem,
|
||||||
'DER': cls._load_pkcs1_der,
|
'DER': cls._load_pkcs1_der,
|
||||||
}
|
}
|
||||||
|
|
||||||
if format not in methods:
|
method = cls._assert_format_exists(format, methods)
|
||||||
formats = ', '.join(sorted(methods.keys()))
|
|
||||||
raise ValueError('Unsupported format: %r, try one of %s' % (format,
|
|
||||||
formats))
|
|
||||||
|
|
||||||
method = methods[format]
|
|
||||||
return method(keyfile)
|
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'):
|
def save_pkcs1(self, format='PEM'):
|
||||||
'''Saves the public key in PKCS#1 DER or PEM format.
|
"""Saves the public key in PKCS#1 DER or PEM format.
|
||||||
|
|
||||||
:param format: the format to save; 'PEM' or 'DER'
|
:param format: the format to save; 'PEM' or 'DER'
|
||||||
:returns: the DER- or PEM-encoded public key.
|
:returns: the DER- or PEM-encoded public key.
|
||||||
|
"""
|
||||||
'''
|
|
||||||
|
|
||||||
methods = {
|
methods = {
|
||||||
'PEM': self._save_pkcs1_pem,
|
'PEM': self._save_pkcs1_pem,
|
||||||
'DER': self._save_pkcs1_der,
|
'DER': self._save_pkcs1_der,
|
||||||
}
|
}
|
||||||
|
|
||||||
if format not in methods:
|
method = self._assert_format_exists(format, methods)
|
||||||
formats = ', '.join(sorted(methods.keys()))
|
|
||||||
raise ValueError('Unsupported format: %r, try one of %s' % (format,
|
|
||||||
formats))
|
|
||||||
|
|
||||||
method = methods[format]
|
|
||||||
return method()
|
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):
|
class PublicKey(AbstractKey):
|
||||||
'''Represents a public RSA key.
|
"""Represents a public RSA key.
|
||||||
|
|
||||||
This key is also known as the 'encryption key'. It contains the 'n' and 'e'
|
This key is also known as the 'encryption key'. It contains the 'n' and 'e'
|
||||||
values.
|
values.
|
||||||
|
@ -107,20 +155,24 @@ class PublicKey(AbstractKey):
|
||||||
>>> key['e']
|
>>> key['e']
|
||||||
3
|
3
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
__slots__ = ('n', 'e')
|
__slots__ = ('n', 'e')
|
||||||
|
|
||||||
def __init__(self, n, e):
|
|
||||||
self.n = n
|
|
||||||
self.e = e
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
return getattr(self, key)
|
return getattr(self, key)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'PublicKey(%i, %i)' % (self.n, self.e)
|
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):
|
def __eq__(self, other):
|
||||||
if other is None:
|
if other is None:
|
||||||
return False
|
return False
|
||||||
|
@ -135,24 +187,24 @@ class PublicKey(AbstractKey):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _load_pkcs1_der(cls, keyfile):
|
def _load_pkcs1_der(cls, keyfile):
|
||||||
r'''Loads a key in PKCS#1 DER format.
|
"""Loads a key in PKCS#1 DER format.
|
||||||
|
|
||||||
@param keyfile: contents of a DER-encoded file that contains the public
|
:param keyfile: contents of a DER-encoded file that contains the public
|
||||||
key.
|
key.
|
||||||
@return: a PublicKey object
|
:return: a PublicKey object
|
||||||
|
|
||||||
First let's construct a DER encoded key:
|
First let's construct a DER encoded key:
|
||||||
|
|
||||||
>>> import base64
|
>>> import base64
|
||||||
>>> b64der = 'MAwCBQCNGmYtAgMBAAE='
|
>>> b64der = 'MAwCBQCNGmYtAgMBAAE='
|
||||||
>>> der = base64.decodestring(b64der)
|
>>> der = base64.standard_b64decode(b64der)
|
||||||
|
|
||||||
This loads the file:
|
This loads the file:
|
||||||
|
|
||||||
>>> PublicKey._load_pkcs1_der(der)
|
>>> PublicKey._load_pkcs1_der(der)
|
||||||
PublicKey(2367317549, 65537)
|
PublicKey(2367317549, 65537)
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
from pyasn1.codec.der import decoder
|
from pyasn1.codec.der import decoder
|
||||||
from rsa.asn1 import AsnPubKey
|
from rsa.asn1 import AsnPubKey
|
||||||
|
@ -161,10 +213,10 @@ class PublicKey(AbstractKey):
|
||||||
return cls(n=int(priv['modulus']), e=int(priv['publicExponent']))
|
return cls(n=int(priv['modulus']), e=int(priv['publicExponent']))
|
||||||
|
|
||||||
def _save_pkcs1_der(self):
|
def _save_pkcs1_der(self):
|
||||||
'''Saves the public key in PKCS#1 DER format.
|
"""Saves the public key in PKCS#1 DER format.
|
||||||
|
|
||||||
@returns: the DER-encoded public key.
|
@returns: the DER-encoded public key.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
from pyasn1.codec.der import encoder
|
from pyasn1.codec.der import encoder
|
||||||
from rsa.asn1 import AsnPubKey
|
from rsa.asn1 import AsnPubKey
|
||||||
|
@ -178,31 +230,31 @@ class PublicKey(AbstractKey):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _load_pkcs1_pem(cls, keyfile):
|
def _load_pkcs1_pem(cls, keyfile):
|
||||||
'''Loads a PKCS#1 PEM-encoded public key file.
|
"""Loads a PKCS#1 PEM-encoded public key file.
|
||||||
|
|
||||||
The contents of the file before the "-----BEGIN RSA PUBLIC KEY-----" and
|
The contents of the file before the "-----BEGIN RSA PUBLIC KEY-----" and
|
||||||
after the "-----END RSA PUBLIC KEY-----" lines is ignored.
|
after the "-----END RSA PUBLIC KEY-----" lines is ignored.
|
||||||
|
|
||||||
@param keyfile: contents of a PEM-encoded file that contains the public
|
:param keyfile: contents of a PEM-encoded file that contains the public
|
||||||
key.
|
key.
|
||||||
@return: a PublicKey object
|
:return: a PublicKey object
|
||||||
'''
|
"""
|
||||||
|
|
||||||
der = rsa.pem.load_pem(keyfile, 'RSA PUBLIC KEY')
|
der = rsa.pem.load_pem(keyfile, 'RSA PUBLIC KEY')
|
||||||
return cls._load_pkcs1_der(der)
|
return cls._load_pkcs1_der(der)
|
||||||
|
|
||||||
def _save_pkcs1_pem(self):
|
def _save_pkcs1_pem(self):
|
||||||
'''Saves a PKCS#1 PEM-encoded public key file.
|
"""Saves a PKCS#1 PEM-encoded public key file.
|
||||||
|
|
||||||
@return: contents of a PEM-encoded file that contains the public key.
|
:return: contents of a PEM-encoded file that contains the public key.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
der = self._save_pkcs1_der()
|
der = self._save_pkcs1_der()
|
||||||
return rsa.pem.save_pem(der, 'RSA PUBLIC KEY')
|
return rsa.pem.save_pem(der, 'RSA PUBLIC KEY')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_pkcs1_openssl_pem(cls, keyfile):
|
def load_pkcs1_openssl_pem(cls, keyfile):
|
||||||
'''Loads a PKCS#1.5 PEM-encoded public key file from OpenSSL.
|
"""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
|
These files can be recognised in that they start with BEGIN PUBLIC KEY
|
||||||
rather than BEGIN RSA PUBLIC KEY.
|
rather than BEGIN RSA PUBLIC KEY.
|
||||||
|
@ -210,22 +262,23 @@ class PublicKey(AbstractKey):
|
||||||
The contents of the file before the "-----BEGIN PUBLIC KEY-----" and
|
The contents of the file before the "-----BEGIN PUBLIC KEY-----" and
|
||||||
after the "-----END PUBLIC KEY-----" lines is ignored.
|
after the "-----END PUBLIC KEY-----" lines is ignored.
|
||||||
|
|
||||||
@param keyfile: contents of a PEM-encoded file that contains the public
|
:param keyfile: contents of a PEM-encoded file that contains the public
|
||||||
key, from OpenSSL.
|
key, from OpenSSL.
|
||||||
@return: a PublicKey object
|
:return: a PublicKey object
|
||||||
'''
|
"""
|
||||||
|
|
||||||
der = rsa.pem.load_pem(keyfile, 'PUBLIC KEY')
|
der = rsa.pem.load_pem(keyfile, 'PUBLIC KEY')
|
||||||
return cls.load_pkcs1_openssl_der(der)
|
return cls.load_pkcs1_openssl_der(der)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_pkcs1_openssl_der(cls, keyfile):
|
def load_pkcs1_openssl_der(cls, keyfile):
|
||||||
'''Loads a PKCS#1 DER-encoded public key file from OpenSSL.
|
"""Loads a PKCS#1 DER-encoded public key file from OpenSSL.
|
||||||
|
|
||||||
@param keyfile: contents of a DER-encoded file that contains the public
|
:param keyfile: contents of a DER-encoded file that contains the public
|
||||||
key, from OpenSSL.
|
key, from OpenSSL.
|
||||||
@return: a PublicKey object
|
:return: a PublicKey object
|
||||||
'''
|
|
||||||
|
"""
|
||||||
|
|
||||||
from rsa.asn1 import OpenSSLPubKey
|
from rsa.asn1 import OpenSSLPubKey
|
||||||
from pyasn1.codec.der import decoder
|
from pyasn1.codec.der import decoder
|
||||||
|
@ -239,10 +292,8 @@ class PublicKey(AbstractKey):
|
||||||
return cls._load_pkcs1_der(keyinfo['key'][1:])
|
return cls._load_pkcs1_der(keyinfo['key'][1:])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PrivateKey(AbstractKey):
|
class PrivateKey(AbstractKey):
|
||||||
'''Represents a private RSA key.
|
"""Represents a private RSA key.
|
||||||
|
|
||||||
This key is also known as the 'decryption key'. It contains the 'n', 'e',
|
This key is also known as the 'decryption key'. It contains the 'n', 'e',
|
||||||
'd', 'p', 'q' and other values.
|
'd', 'p', 'q' and other values.
|
||||||
|
@ -253,13 +304,13 @@ class PrivateKey(AbstractKey):
|
||||||
>>> PrivateKey(3247, 65537, 833, 191, 17)
|
>>> PrivateKey(3247, 65537, 833, 191, 17)
|
||||||
PrivateKey(3247, 65537, 833, 191, 17)
|
PrivateKey(3247, 65537, 833, 191, 17)
|
||||||
|
|
||||||
exp1, exp2 and coef don't have to be given, they will be calculated:
|
exp1, exp2 and coef can be given, but if None or omitted they will be calculated:
|
||||||
|
|
||||||
>>> pk = PrivateKey(3727264081, 65537, 3349121513, 65063, 57287)
|
>>> pk = PrivateKey(3727264081, 65537, 3349121513, 65063, 57287, exp2=4)
|
||||||
>>> pk.exp1
|
>>> pk.exp1
|
||||||
55063
|
55063
|
||||||
>>> pk.exp2
|
>>> pk.exp2 # this is of course not a correct value, but it is the one we passed.
|
||||||
10095
|
4
|
||||||
>>> pk.coef
|
>>> pk.coef
|
||||||
50797
|
50797
|
||||||
|
|
||||||
|
@ -273,13 +324,12 @@ class PrivateKey(AbstractKey):
|
||||||
>>> pk.coef
|
>>> pk.coef
|
||||||
8
|
8
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
__slots__ = ('n', 'e', 'd', 'p', 'q', 'exp1', 'exp2', 'coef')
|
__slots__ = ('n', 'e', 'd', 'p', 'q', 'exp1', 'exp2', 'coef')
|
||||||
|
|
||||||
def __init__(self, n, e, d, p, q, exp1=None, exp2=None, coef=None):
|
def __init__(self, n, e, d, p, q, exp1=None, exp2=None, coef=None):
|
||||||
self.n = n
|
AbstractKey.__init__(self, n, e)
|
||||||
self.e = e
|
|
||||||
self.d = d
|
self.d = d
|
||||||
self.p = p
|
self.p = p
|
||||||
self.q = q
|
self.q = q
|
||||||
|
@ -290,7 +340,7 @@ class PrivateKey(AbstractKey):
|
||||||
else:
|
else:
|
||||||
self.exp1 = exp1
|
self.exp1 = exp1
|
||||||
|
|
||||||
if exp1 is None:
|
if exp2 is None:
|
||||||
self.exp2 = int(d % (q - 1))
|
self.exp2 = int(d % (q - 1))
|
||||||
else:
|
else:
|
||||||
self.exp2 = exp2
|
self.exp2 = exp2
|
||||||
|
@ -306,6 +356,14 @@ class PrivateKey(AbstractKey):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'PrivateKey(%(n)i, %(e)i, %(d)i, %(p)i, %(q)i)' % 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):
|
def __eq__(self, other):
|
||||||
if other is None:
|
if other is None:
|
||||||
return False
|
return False
|
||||||
|
@ -325,26 +383,57 @@ class PrivateKey(AbstractKey):
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return not (self == other)
|
return not (self == other)
|
||||||
|
|
||||||
|
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 = rsa.randnum.randint(self.n - 1)
|
||||||
|
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 = rsa.randnum.randint(self.n - 1)
|
||||||
|
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
|
@classmethod
|
||||||
def _load_pkcs1_der(cls, keyfile):
|
def _load_pkcs1_der(cls, keyfile):
|
||||||
r'''Loads a key in PKCS#1 DER format.
|
"""Loads a key in PKCS#1 DER format.
|
||||||
|
|
||||||
@param keyfile: contents of a DER-encoded file that contains the private
|
:param keyfile: contents of a DER-encoded file that contains the private
|
||||||
key.
|
key.
|
||||||
@return: a PrivateKey object
|
:return: a PrivateKey object
|
||||||
|
|
||||||
First let's construct a DER encoded key:
|
First let's construct a DER encoded key:
|
||||||
|
|
||||||
>>> import base64
|
>>> import base64
|
||||||
>>> b64der = 'MC4CAQACBQDeKYlRAgMBAAECBQDHn4npAgMA/icCAwDfxwIDANcXAgInbwIDAMZt'
|
>>> b64der = 'MC4CAQACBQDeKYlRAgMBAAECBQDHn4npAgMA/icCAwDfxwIDANcXAgInbwIDAMZt'
|
||||||
>>> der = base64.decodestring(b64der)
|
>>> der = base64.standard_b64decode(b64der)
|
||||||
|
|
||||||
This loads the file:
|
This loads the file:
|
||||||
|
|
||||||
>>> PrivateKey._load_pkcs1_der(der)
|
>>> PrivateKey._load_pkcs1_der(der)
|
||||||
PrivateKey(3727264081, 65537, 3349121513, 65063, 57287)
|
PrivateKey(3727264081, 65537, 3349121513, 65063, 57287)
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
from pyasn1.codec.der import decoder
|
from pyasn1.codec.der import decoder
|
||||||
(priv, _) = decoder.decode(keyfile)
|
(priv, _) = decoder.decode(keyfile)
|
||||||
|
@ -371,10 +460,10 @@ class PrivateKey(AbstractKey):
|
||||||
return cls(*as_ints)
|
return cls(*as_ints)
|
||||||
|
|
||||||
def _save_pkcs1_der(self):
|
def _save_pkcs1_der(self):
|
||||||
'''Saves the private key in PKCS#1 DER format.
|
"""Saves the private key in PKCS#1 DER format.
|
||||||
|
|
||||||
@returns: the DER-encoded private key.
|
@returns: the DER-encoded private key.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
from pyasn1.type import univ, namedtype
|
from pyasn1.type import univ, namedtype
|
||||||
from pyasn1.codec.der import encoder
|
from pyasn1.codec.der import encoder
|
||||||
|
@ -408,30 +497,31 @@ class PrivateKey(AbstractKey):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _load_pkcs1_pem(cls, keyfile):
|
def _load_pkcs1_pem(cls, keyfile):
|
||||||
'''Loads a PKCS#1 PEM-encoded private key file.
|
"""Loads a PKCS#1 PEM-encoded private key file.
|
||||||
|
|
||||||
The contents of the file before the "-----BEGIN RSA PRIVATE KEY-----" and
|
The contents of the file before the "-----BEGIN RSA PRIVATE KEY-----" and
|
||||||
after the "-----END RSA PRIVATE KEY-----" lines is ignored.
|
after the "-----END RSA PRIVATE KEY-----" lines is ignored.
|
||||||
|
|
||||||
@param keyfile: contents of a PEM-encoded file that contains the private
|
:param keyfile: contents of a PEM-encoded file that contains the private
|
||||||
key.
|
key.
|
||||||
@return: a PrivateKey object
|
:return: a PrivateKey object
|
||||||
'''
|
"""
|
||||||
|
|
||||||
der = rsa.pem.load_pem(keyfile, b('RSA PRIVATE KEY'))
|
der = rsa.pem.load_pem(keyfile, b('RSA PRIVATE KEY'))
|
||||||
return cls._load_pkcs1_der(der)
|
return cls._load_pkcs1_der(der)
|
||||||
|
|
||||||
def _save_pkcs1_pem(self):
|
def _save_pkcs1_pem(self):
|
||||||
'''Saves a PKCS#1 PEM-encoded private key file.
|
"""Saves a PKCS#1 PEM-encoded private key file.
|
||||||
|
|
||||||
@return: contents of a PEM-encoded file that contains the private key.
|
:return: contents of a PEM-encoded file that contains the private key.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
der = self._save_pkcs1_der()
|
der = self._save_pkcs1_der()
|
||||||
return rsa.pem.save_pem(der, b('RSA PRIVATE KEY'))
|
return rsa.pem.save_pem(der, b('RSA PRIVATE KEY'))
|
||||||
|
|
||||||
|
|
||||||
def find_p_q(nbits, getprime_func=rsa.prime.getprime, accurate=True):
|
def find_p_q(nbits, getprime_func=rsa.prime.getprime, accurate=True):
|
||||||
''''Returns a tuple of two different primes of nbits bits each.
|
"""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
|
The resulting p * q has exacty 2 * nbits bits, and the returned p and q
|
||||||
will not be equal.
|
will not be equal.
|
||||||
|
@ -459,7 +549,7 @@ def find_p_q(nbits, getprime_func=rsa.prime.getprime, accurate=True):
|
||||||
>>> common.bit_size(p * q) > 240
|
>>> common.bit_size(p * q) > 240
|
||||||
True
|
True
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
total_bits = nbits * 2
|
total_bits = nbits * 2
|
||||||
|
|
||||||
|
@ -476,11 +566,11 @@ def find_p_q(nbits, getprime_func=rsa.prime.getprime, accurate=True):
|
||||||
q = getprime_func(qbits)
|
q = getprime_func(qbits)
|
||||||
|
|
||||||
def is_acceptable(p, q):
|
def is_acceptable(p, q):
|
||||||
'''Returns True iff p and q are acceptable:
|
"""Returns True iff p and q are acceptable:
|
||||||
|
|
||||||
- p and q differ
|
- p and q differ
|
||||||
- (p * q) has the right nr of bits (when accurate=True)
|
- (p * q) has the right nr of bits (when accurate=True)
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if p == q:
|
if p == q:
|
||||||
return False
|
return False
|
||||||
|
@ -505,33 +595,52 @@ def find_p_q(nbits, getprime_func=rsa.prime.getprime, accurate=True):
|
||||||
|
|
||||||
# We want p > q as described on
|
# We want p > q as described on
|
||||||
# http://www.di-mgt.com.au/rsa_alg.html#crt
|
# http://www.di-mgt.com.au/rsa_alg.html#crt
|
||||||
return (max(p, q), min(p, q))
|
return max(p, q), min(p, q)
|
||||||
|
|
||||||
def calculate_keys(p, q, nbits):
|
|
||||||
'''Calculates an encryption and a decryption key given p and q, and
|
|
||||||
returns them as a tuple (e, d)
|
|
||||||
|
|
||||||
'''
|
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)
|
phi_n = (p - 1) * (q - 1)
|
||||||
|
|
||||||
# A very common choice for e is 65537
|
|
||||||
e = 65537
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
d = rsa.common.inverse(e, phi_n)
|
d = rsa.common.inverse(exponent, phi_n)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValueError("e (%d) and phi_n (%d) are not relatively prime" %
|
raise ValueError("e (%d) and phi_n (%d) are not relatively prime" %
|
||||||
(e, phi_n))
|
(exponent, phi_n))
|
||||||
|
|
||||||
if (e * d) % phi_n != 1:
|
if (exponent * d) % phi_n != 1:
|
||||||
raise ValueError("e (%d) and d (%d) are not mult. inv. modulo "
|
raise ValueError("e (%d) and d (%d) are not mult. inv. modulo "
|
||||||
"phi_n (%d)" % (e, d, phi_n))
|
"phi_n (%d)" % (exponent, d, phi_n))
|
||||||
|
|
||||||
return (e, d)
|
return exponent, d
|
||||||
|
|
||||||
def gen_keys(nbits, getprime_func, accurate=True):
|
|
||||||
'''Generate RSA keys of nbits bits. Returns (p, q, e, 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.
|
Note: this can take a long time, depending on the key size.
|
||||||
|
|
||||||
|
@ -539,15 +648,27 @@ def gen_keys(nbits, getprime_func, accurate=True):
|
||||||
``q`` will use ``nbits/2`` bits.
|
``q`` will use ``nbits/2`` bits.
|
||||||
:param getprime_func: either :py:func:`rsa.prime.getprime` or a function
|
:param getprime_func: either :py:func:`rsa.prime.getprime` or a function
|
||||||
with similar signature.
|
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)
|
(p, q) = find_p_q(nbits // 2, getprime_func, accurate)
|
||||||
(e, d) = calculate_keys(p, q, nbits // 2)
|
try:
|
||||||
|
(e, d) = calculate_keys_custom_exponent(p, q, exponent=exponent)
|
||||||
|
break
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
return (p, q, e, d)
|
return p, q, e, d
|
||||||
|
|
||||||
def newkeys(nbits, accurate=True, poolsize=1):
|
|
||||||
'''Generates public and private keys, and returns them as (pub, priv).
|
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
|
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
|
:py:class:`rsa.PublicKey` object. The private key is also known as the
|
||||||
|
@ -560,13 +681,17 @@ def newkeys(nbits, accurate=True, poolsize=1):
|
||||||
:param poolsize: the number of processes to use to generate the prime
|
: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.
|
numbers. If set to a number > 1, a parallel algorithm will be used.
|
||||||
This requires Python 2.6 or newer.
|
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`)
|
:returns: a tuple (:py:class:`rsa.PublicKey`, :py:class:`rsa.PrivateKey`)
|
||||||
|
|
||||||
The ``poolsize`` parameter was added in *Python-RSA 3.1* and requires
|
The ``poolsize`` parameter was added in *Python-RSA 3.1* and requires
|
||||||
Python 2.6 or newer.
|
Python 2.6 or newer.
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if nbits < 16:
|
if nbits < 16:
|
||||||
raise ValueError('Key too small')
|
raise ValueError('Key too small')
|
||||||
|
@ -580,10 +705,11 @@ def newkeys(nbits, accurate=True, poolsize=1):
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
getprime_func = functools.partial(parallel.getprime, poolsize=poolsize)
|
getprime_func = functools.partial(parallel.getprime, poolsize=poolsize)
|
||||||
else: getprime_func = rsa.prime.getprime
|
else:
|
||||||
|
getprime_func = rsa.prime.getprime
|
||||||
|
|
||||||
# Generate the key components
|
# Generate the key components
|
||||||
(p, q, e, d) = gen_keys(nbits, getprime_func)
|
(p, q, e, d) = gen_keys(nbits, getprime_func, accurate=accurate, exponent=exponent)
|
||||||
|
|
||||||
# Create the key objects
|
# Create the key objects
|
||||||
n = p * q
|
n = p * q
|
||||||
|
@ -593,6 +719,7 @@ def newkeys(nbits, accurate=True, poolsize=1):
|
||||||
PrivateKey(n, e, d, p, q)
|
PrivateKey(n, e, d, p, q)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['PublicKey', 'PrivateKey', 'newkeys']
|
__all__ = ['PublicKey', 'PrivateKey', 'newkeys']
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
'''Functions for parallel computation on multiple cores.
|
"""Functions for parallel computation on multiple cores.
|
||||||
|
|
||||||
Introduced in Python-RSA 3.1.
|
Introduced in Python-RSA 3.1.
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ Introduced in Python-RSA 3.1.
|
||||||
|
|
||||||
Requires Python 2.6 or newer.
|
Requires Python 2.6 or newer.
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
@ -31,20 +31,19 @@ import multiprocessing as mp
|
||||||
import rsa.prime
|
import rsa.prime
|
||||||
import rsa.randnum
|
import rsa.randnum
|
||||||
|
|
||||||
|
|
||||||
def _find_prime(nbits, pipe):
|
def _find_prime(nbits, pipe):
|
||||||
while True:
|
while True:
|
||||||
integer = rsa.randnum.read_random_int(nbits)
|
integer = rsa.randnum.read_random_odd_int(nbits)
|
||||||
|
|
||||||
# Make sure it's odd
|
|
||||||
integer |= 1
|
|
||||||
|
|
||||||
# Test for primeness
|
# Test for primeness
|
||||||
if rsa.prime.is_prime(integer):
|
if rsa.prime.is_prime(integer):
|
||||||
pipe.send(integer)
|
pipe.send(integer)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def getprime(nbits, poolsize):
|
def getprime(nbits, poolsize):
|
||||||
'''Returns a prime number that can be stored in 'nbits' bits.
|
"""Returns a prime number that can be stored in 'nbits' bits.
|
||||||
|
|
||||||
Works in multiple threads at the same time.
|
Works in multiple threads at the same time.
|
||||||
|
|
||||||
|
@ -60,23 +59,31 @@ def getprime(nbits, poolsize):
|
||||||
>>> common.bit_size(p) == 128
|
>>> common.bit_size(p) == 128
|
||||||
True
|
True
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
(pipe_recv, pipe_send) = mp.Pipe(duplex=False)
|
(pipe_recv, pipe_send) = mp.Pipe(duplex=False)
|
||||||
|
|
||||||
# Create processes
|
# Create processes
|
||||||
|
try:
|
||||||
procs = [mp.Process(target=_find_prime, args=(nbits, pipe_send))
|
procs = [mp.Process(target=_find_prime, args=(nbits, pipe_send))
|
||||||
for _ in range(poolsize)]
|
for _ in range(poolsize)]
|
||||||
[p.start() for p in procs]
|
# Start processes
|
||||||
|
for p in procs:
|
||||||
|
p.start()
|
||||||
|
|
||||||
result = pipe_recv.recv()
|
result = pipe_recv.recv()
|
||||||
|
finally:
|
||||||
|
pipe_recv.close()
|
||||||
|
pipe_send.close()
|
||||||
|
|
||||||
[p.terminate() for p in procs]
|
# Terminate processes
|
||||||
|
for p in procs:
|
||||||
|
p.terminate()
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
__all__ = ['getprime']
|
|
||||||
|
|
||||||
|
__all__ = ['getprime']
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print('Running doctests 1000x or until failure')
|
print('Running doctests 1000x or until failure')
|
||||||
|
@ -91,4 +98,3 @@ if __name__ == '__main__':
|
||||||
print('%i times' % count)
|
print('%i times' % count)
|
||||||
|
|
||||||
print('Doctests done')
|
print('Doctests done')
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -14,15 +14,16 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
'''Functions that load and write PEM-encoded files.'''
|
"""Functions that load and write PEM-encoded files."""
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
from rsa._compat import b, is_bytes
|
from rsa._compat import b, is_bytes
|
||||||
|
|
||||||
|
|
||||||
def _markers(pem_marker):
|
def _markers(pem_marker):
|
||||||
'''
|
"""
|
||||||
Returns the start and end PEM markers
|
Returns the start and end PEM markers
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if is_bytes(pem_marker):
|
if is_bytes(pem_marker):
|
||||||
pem_marker = pem_marker.decode('utf-8')
|
pem_marker = pem_marker.decode('utf-8')
|
||||||
|
@ -30,20 +31,25 @@ def _markers(pem_marker):
|
||||||
return (b('-----BEGIN %s-----' % pem_marker),
|
return (b('-----BEGIN %s-----' % pem_marker),
|
||||||
b('-----END %s-----' % pem_marker))
|
b('-----END %s-----' % pem_marker))
|
||||||
|
|
||||||
def load_pem(contents, pem_marker):
|
|
||||||
'''Loads a PEM file.
|
|
||||||
|
|
||||||
@param contents: the contents of the file to interpret
|
def load_pem(contents, pem_marker):
|
||||||
@param pem_marker: the marker of the PEM content, such as 'RSA PRIVATE KEY'
|
"""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
|
when your file has '-----BEGIN RSA PRIVATE KEY-----' and
|
||||||
'-----END RSA PRIVATE KEY-----' markers.
|
'-----END RSA PRIVATE KEY-----' markers.
|
||||||
|
|
||||||
@return the base64-decoded content between the start and end markers.
|
:return: the base64-decoded content between the start and end markers.
|
||||||
|
|
||||||
@raise ValueError: when the content is invalid, for example when the start
|
@raise ValueError: when the content is invalid, for example when the start
|
||||||
marker cannot be found.
|
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_start, pem_end) = _markers(pem_marker)
|
||||||
|
|
||||||
|
@ -89,24 +95,24 @@ def load_pem(contents, pem_marker):
|
||||||
|
|
||||||
# Base64-decode the contents
|
# Base64-decode the contents
|
||||||
pem = b('').join(pem_lines)
|
pem = b('').join(pem_lines)
|
||||||
return base64.decodestring(pem)
|
return base64.standard_b64decode(pem)
|
||||||
|
|
||||||
|
|
||||||
def save_pem(contents, pem_marker):
|
def save_pem(contents, pem_marker):
|
||||||
'''Saves a PEM file.
|
"""Saves a PEM file.
|
||||||
|
|
||||||
@param contents: the contents to encode in PEM format
|
:param contents: the contents to encode in PEM format
|
||||||
@param pem_marker: the marker of the PEM content, such as 'RSA PRIVATE KEY'
|
:param pem_marker: the marker of the PEM content, such as 'RSA PRIVATE KEY'
|
||||||
when your file has '-----BEGIN RSA PRIVATE KEY-----' and
|
when your file has '-----BEGIN RSA PRIVATE KEY-----' and
|
||||||
'-----END RSA PRIVATE KEY-----' markers.
|
'-----END RSA PRIVATE KEY-----' markers.
|
||||||
|
|
||||||
@return the base64-encoded content between the start and end markers.
|
:return: the base64-encoded content between the start and end markers.
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
(pem_start, pem_end) = _markers(pem_marker)
|
(pem_start, pem_end) = _markers(pem_marker)
|
||||||
|
|
||||||
b64 = base64.encodestring(contents).replace(b('\n'), b(''))
|
b64 = base64.standard_b64encode(contents).replace(b('\n'), b(''))
|
||||||
pem_lines = [pem_start]
|
pem_lines = [pem_start]
|
||||||
|
|
||||||
for block_start in range(0, len(b64), 64):
|
for block_start in range(0, len(b64), 64):
|
||||||
|
@ -117,4 +123,3 @@ def save_pem(contents, pem_marker):
|
||||||
pem_lines.append(b(''))
|
pem_lines.append(b(''))
|
||||||
|
|
||||||
return b('\n').join(pem_lines)
|
return b('\n').join(pem_lines)
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
'''Functions for PKCS#1 version 1.5 encryption and signing
|
"""Functions for PKCS#1 version 1.5 encryption and signing
|
||||||
|
|
||||||
This module implements certain functionality from PKCS#1 version 1.5. For a
|
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
|
very clear example, read http://www.di-mgt.com.au/rsa_alg.html#pkcs1schemes
|
||||||
|
@ -22,17 +22,17 @@ 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
|
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.
|
these methods much more secure than the ones in the ``rsa`` module.
|
||||||
|
|
||||||
WARNING: this module leaks information when decryption or verification fails.
|
WARNING: this module leaks information when decryption fails. The exceptions
|
||||||
The exceptions that are raised contain the Python traceback information, which
|
that are raised contain the Python traceback information, which can be used to
|
||||||
can be used to deduce where in the process the failure occurred. DO NOT PASS
|
deduce where in the process the failure occurred. DO NOT PASS SUCH INFORMATION
|
||||||
SUCH INFORMATION to your users.
|
to your users.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from rsa._compat import b
|
from rsa._compat import b
|
||||||
from rsa import common, transform, core, varblock
|
from rsa import common, transform, core
|
||||||
|
|
||||||
# ASN.1 codes that describe the hash algorithm used.
|
# ASN.1 codes that describe the hash algorithm used.
|
||||||
HASH_ASN1 = {
|
HASH_ASN1 = {
|
||||||
|
@ -51,29 +51,33 @@ HASH_METHODS = {
|
||||||
'SHA-512': hashlib.sha512,
|
'SHA-512': hashlib.sha512,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class CryptoError(Exception):
|
class CryptoError(Exception):
|
||||||
'''Base class for all exceptions in this module.'''
|
"""Base class for all exceptions in this module."""
|
||||||
|
|
||||||
|
|
||||||
class DecryptionError(CryptoError):
|
class DecryptionError(CryptoError):
|
||||||
'''Raised when decryption fails.'''
|
"""Raised when decryption fails."""
|
||||||
|
|
||||||
|
|
||||||
class VerificationError(CryptoError):
|
class VerificationError(CryptoError):
|
||||||
'''Raised when verification fails.'''
|
"""Raised when verification fails."""
|
||||||
|
|
||||||
|
|
||||||
def _pad_for_encryption(message, target_length):
|
def _pad_for_encryption(message, target_length):
|
||||||
r'''Pads the message for encryption, returning the padded message.
|
r"""Pads the message for encryption, returning the padded message.
|
||||||
|
|
||||||
:return: 00 02 RANDOM_DATA 00 MESSAGE
|
:return: 00 02 RANDOM_DATA 00 MESSAGE
|
||||||
|
|
||||||
>>> block = _pad_for_encryption('hello', 16)
|
>>> block = _pad_for_encryption(b'hello', 16)
|
||||||
>>> len(block)
|
>>> len(block)
|
||||||
16
|
16
|
||||||
>>> block[0:2]
|
>>> block[0:2]
|
||||||
'\x00\x02'
|
b'\x00\x02'
|
||||||
>>> block[-6:]
|
>>> block[-6:]
|
||||||
'\x00hello'
|
b'\x00hello'
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
max_msglength = target_length - 11
|
max_msglength = target_length - 11
|
||||||
msglength = len(message)
|
msglength = len(message)
|
||||||
|
@ -107,23 +111,23 @@ def _pad_for_encryption(message, target_length):
|
||||||
|
|
||||||
|
|
||||||
def _pad_for_signing(message, target_length):
|
def _pad_for_signing(message, target_length):
|
||||||
r'''Pads the message for signing, returning the padded message.
|
r"""Pads the message for signing, returning the padded message.
|
||||||
|
|
||||||
The padding is always a repetition of FF bytes.
|
The padding is always a repetition of FF bytes.
|
||||||
|
|
||||||
:return: 00 01 PADDING 00 MESSAGE
|
:return: 00 01 PADDING 00 MESSAGE
|
||||||
|
|
||||||
>>> block = _pad_for_signing('hello', 16)
|
>>> block = _pad_for_signing(b'hello', 16)
|
||||||
>>> len(block)
|
>>> len(block)
|
||||||
16
|
16
|
||||||
>>> block[0:2]
|
>>> block[0:2]
|
||||||
'\x00\x01'
|
b'\x00\x01'
|
||||||
>>> block[-6:]
|
>>> block[-6:]
|
||||||
'\x00hello'
|
b'\x00hello'
|
||||||
>>> block[2:-6]
|
>>> block[2:-6]
|
||||||
'\xff\xff\xff\xff\xff\xff\xff\xff'
|
b'\xff\xff\xff\xff\xff\xff\xff\xff'
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
max_msglength = target_length - 11
|
max_msglength = target_length - 11
|
||||||
msglength = len(message)
|
msglength = len(message)
|
||||||
|
@ -141,7 +145,7 @@ def _pad_for_signing(message, target_length):
|
||||||
|
|
||||||
|
|
||||||
def encrypt(message, pub_key):
|
def encrypt(message, pub_key):
|
||||||
'''Encrypts the given message using PKCS#1 v1.5
|
"""Encrypts the given message using PKCS#1 v1.5
|
||||||
|
|
||||||
:param message: the message to encrypt. Must be a byte string no longer than
|
: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
|
``k-11`` bytes, where ``k`` is the number of bytes needed to encode
|
||||||
|
@ -152,7 +156,7 @@ def encrypt(message, pub_key):
|
||||||
|
|
||||||
>>> from rsa import key, common
|
>>> from rsa import key, common
|
||||||
>>> (pub_key, priv_key) = key.newkeys(256)
|
>>> (pub_key, priv_key) = key.newkeys(256)
|
||||||
>>> message = 'hello'
|
>>> message = b'hello'
|
||||||
>>> crypto = encrypt(message, pub_key)
|
>>> crypto = encrypt(message, pub_key)
|
||||||
|
|
||||||
The crypto text should be just as long as the public key 'n' component:
|
The crypto text should be just as long as the public key 'n' component:
|
||||||
|
@ -160,7 +164,7 @@ def encrypt(message, pub_key):
|
||||||
>>> len(crypto) == common.byte_size(pub_key.n)
|
>>> len(crypto) == common.byte_size(pub_key.n)
|
||||||
True
|
True
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
keylength = common.byte_size(pub_key.n)
|
keylength = common.byte_size(pub_key.n)
|
||||||
padded = _pad_for_encryption(message, keylength)
|
padded = _pad_for_encryption(message, keylength)
|
||||||
|
@ -171,8 +175,9 @@ def encrypt(message, pub_key):
|
||||||
|
|
||||||
return block
|
return block
|
||||||
|
|
||||||
|
|
||||||
def decrypt(crypto, priv_key):
|
def decrypt(crypto, priv_key):
|
||||||
r'''Decrypts the given message using PKCS#1 v1.5
|
r"""Decrypts the given message using PKCS#1 v1.5
|
||||||
|
|
||||||
The decryption is considered 'failed' when the resulting cleartext doesn't
|
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
|
start with the bytes 00 02, or when the 00 byte between the padding and
|
||||||
|
@ -190,15 +195,15 @@ def decrypt(crypto, priv_key):
|
||||||
|
|
||||||
It works with strings:
|
It works with strings:
|
||||||
|
|
||||||
>>> crypto = encrypt('hello', pub_key)
|
>>> crypto = encrypt(b'hello', pub_key)
|
||||||
>>> decrypt(crypto, priv_key)
|
>>> decrypt(crypto, priv_key)
|
||||||
'hello'
|
b'hello'
|
||||||
|
|
||||||
And with binary data:
|
And with binary data:
|
||||||
|
|
||||||
>>> crypto = encrypt('\x00\x00\x00\x00\x01', pub_key)
|
>>> crypto = encrypt(b'\x00\x00\x00\x00\x01', pub_key)
|
||||||
>>> decrypt(crypto, priv_key)
|
>>> decrypt(crypto, priv_key)
|
||||||
'\x00\x00\x00\x00\x01'
|
b'\x00\x00\x00\x00\x01'
|
||||||
|
|
||||||
Altering the encrypted information will *likely* cause a
|
Altering the encrypted information will *likely* cause a
|
||||||
:py:class:`rsa.pkcs1.DecryptionError`. If you want to be *sure*, use
|
:py:class:`rsa.pkcs1.DecryptionError`. If you want to be *sure*, use
|
||||||
|
@ -213,18 +218,18 @@ def decrypt(crypto, priv_key):
|
||||||
It's only a tiny bit of information, but every bit makes cracking the
|
It's only a tiny bit of information, but every bit makes cracking the
|
||||||
keys easier.
|
keys easier.
|
||||||
|
|
||||||
>>> crypto = encrypt('hello', pub_key)
|
>>> crypto = encrypt(b'hello', pub_key)
|
||||||
>>> crypto = crypto[0:5] + 'X' + crypto[6:] # change a byte
|
>>> crypto = crypto[0:5] + b'X' + crypto[6:] # change a byte
|
||||||
>>> decrypt(crypto, priv_key)
|
>>> decrypt(crypto, priv_key)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
DecryptionError: Decryption failed
|
rsa.pkcs1.DecryptionError: Decryption failed
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
blocksize = common.byte_size(priv_key.n)
|
blocksize = common.byte_size(priv_key.n)
|
||||||
encrypted = transform.bytes2int(crypto)
|
encrypted = transform.bytes2int(crypto)
|
||||||
decrypted = core.decrypt_int(encrypted, priv_key.d, priv_key.n)
|
decrypted = priv_key.blinded_decrypt(encrypted)
|
||||||
cleartext = transform.int2bytes(decrypted, blocksize)
|
cleartext = transform.int2bytes(decrypted, blocksize)
|
||||||
|
|
||||||
# If we can't find the cleartext marker, decryption failed.
|
# If we can't find the cleartext marker, decryption failed.
|
||||||
|
@ -237,10 +242,11 @@ def decrypt(crypto, priv_key):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise DecryptionError('Decryption failed')
|
raise DecryptionError('Decryption failed')
|
||||||
|
|
||||||
return cleartext[sep_idx+1:]
|
return cleartext[sep_idx + 1:]
|
||||||
|
|
||||||
|
|
||||||
def sign(message, priv_key, hash):
|
def sign(message, priv_key, hash):
|
||||||
'''Signs the message with the private key.
|
"""Signs the message with the private key.
|
||||||
|
|
||||||
Hashes the message, then signs the hash with the given key. This is known
|
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.
|
as a "detached signature", because the message itself isn't altered.
|
||||||
|
@ -255,7 +261,7 @@ def sign(message, priv_key, hash):
|
||||||
:raise OverflowError: if the private key is too small to contain the
|
:raise OverflowError: if the private key is too small to contain the
|
||||||
requested hash.
|
requested hash.
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
# Get the ASN1 code for this hash method
|
# Get the ASN1 code for this hash method
|
||||||
if hash not in HASH_ASN1:
|
if hash not in HASH_ASN1:
|
||||||
|
@ -271,13 +277,14 @@ def sign(message, priv_key, hash):
|
||||||
padded = _pad_for_signing(cleartext, keylength)
|
padded = _pad_for_signing(cleartext, keylength)
|
||||||
|
|
||||||
payload = transform.bytes2int(padded)
|
payload = transform.bytes2int(padded)
|
||||||
encrypted = core.encrypt_int(payload, priv_key.d, priv_key.n)
|
encrypted = priv_key.blinded_encrypt(payload)
|
||||||
block = transform.int2bytes(encrypted, keylength)
|
block = transform.int2bytes(encrypted, keylength)
|
||||||
|
|
||||||
return block
|
return block
|
||||||
|
|
||||||
|
|
||||||
def verify(message, signature, pub_key):
|
def verify(message, signature, pub_key):
|
||||||
'''Verifies that the signature matches the message.
|
"""Verifies that the signature matches the message.
|
||||||
|
|
||||||
The hash method is detected automatically from the signature.
|
The hash method is detected automatically from the signature.
|
||||||
|
|
||||||
|
@ -288,43 +295,30 @@ def verify(message, signature, pub_key):
|
||||||
:param pub_key: the :py:class:`rsa.PublicKey` of the person signing the message.
|
:param pub_key: the :py:class:`rsa.PublicKey` of the person signing the message.
|
||||||
:raise VerificationError: when the signature doesn't match the message.
|
:raise VerificationError: when the signature doesn't match the message.
|
||||||
|
|
||||||
.. warning::
|
"""
|
||||||
|
|
||||||
Never display the stack trace of a
|
keylength = common.byte_size(pub_key.n)
|
||||||
:py:class:`rsa.pkcs1.VerificationError` 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.
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
blocksize = common.byte_size(pub_key.n)
|
|
||||||
encrypted = transform.bytes2int(signature)
|
encrypted = transform.bytes2int(signature)
|
||||||
decrypted = core.decrypt_int(encrypted, pub_key.e, pub_key.n)
|
decrypted = core.decrypt_int(encrypted, pub_key.e, pub_key.n)
|
||||||
clearsig = transform.int2bytes(decrypted, blocksize)
|
clearsig = transform.int2bytes(decrypted, keylength)
|
||||||
|
|
||||||
# If we can't find the signature marker, verification failed.
|
# Get the hash method
|
||||||
if clearsig[0:2] != b('\x00\x01'):
|
method_name = _find_method_hash(clearsig)
|
||||||
raise VerificationError('Verification failed')
|
|
||||||
|
|
||||||
# Find the 00 separator between the padding and the payload
|
|
||||||
try:
|
|
||||||
sep_idx = clearsig.index(b('\x00'), 2)
|
|
||||||
except ValueError:
|
|
||||||
raise VerificationError('Verification failed')
|
|
||||||
|
|
||||||
# Get the hash and the hash method
|
|
||||||
(method_name, signature_hash) = _find_method_hash(clearsig[sep_idx+1:])
|
|
||||||
message_hash = _hash(message, method_name)
|
message_hash = _hash(message, method_name)
|
||||||
|
|
||||||
# Compare the real hash to the hash in the signature
|
# Reconstruct the expected padded hash
|
||||||
if message_hash != signature_hash:
|
cleartext = HASH_ASN1[method_name] + message_hash
|
||||||
|
expected = _pad_for_signing(cleartext, keylength)
|
||||||
|
|
||||||
|
# Compare with the signed one
|
||||||
|
if expected != clearsig:
|
||||||
raise VerificationError('Verification failed')
|
raise VerificationError('Verification failed')
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _hash(message, method_name):
|
def _hash(message, method_name):
|
||||||
'''Returns the message digest.
|
"""Returns the message digest.
|
||||||
|
|
||||||
:param message: the signed message. Can be an 8-bit string or a file-like
|
: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
|
object. If ``message`` has a ``read()`` method, it is assumed to be a
|
||||||
|
@ -332,7 +326,7 @@ def _hash(message, method_name):
|
||||||
:param method_name: the hash method, must be a key of
|
:param method_name: the hash method, must be a key of
|
||||||
:py:const:`HASH_METHODS`.
|
:py:const:`HASH_METHODS`.
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if method_name not in HASH_METHODS:
|
if method_name not in HASH_METHODS:
|
||||||
raise ValueError('Invalid hash method: %s' % method_name)
|
raise ValueError('Invalid hash method: %s' % method_name)
|
||||||
|
@ -341,6 +335,9 @@ def _hash(message, method_name):
|
||||||
hasher = method()
|
hasher = method()
|
||||||
|
|
||||||
if hasattr(message, 'read') and hasattr(message.read, '__call__'):
|
if hasattr(message, 'read') and hasattr(message.read, '__call__'):
|
||||||
|
# Late import to prevent DeprecationWarnings.
|
||||||
|
from . import varblock
|
||||||
|
|
||||||
# read as 1K blocks
|
# read as 1K blocks
|
||||||
for block in varblock.yield_fixedblocks(message, 1024):
|
for block in varblock.yield_fixedblocks(message, 1024):
|
||||||
hasher.update(block)
|
hasher.update(block)
|
||||||
|
@ -351,24 +348,17 @@ def _hash(message, method_name):
|
||||||
return hasher.digest()
|
return hasher.digest()
|
||||||
|
|
||||||
|
|
||||||
def _find_method_hash(method_hash):
|
def _find_method_hash(clearsig):
|
||||||
'''Finds the hash method and the hash itself.
|
"""Finds the hash method.
|
||||||
|
|
||||||
:param method_hash: ASN1 code for the hash method concatenated with the
|
|
||||||
hash itself.
|
|
||||||
|
|
||||||
:return: tuple (method, hash) where ``method`` is the used hash method, and
|
|
||||||
``hash`` is the hash itself.
|
|
||||||
|
|
||||||
|
:param clearsig: full padded ASN1 and hash.
|
||||||
|
:return: the used hash method.
|
||||||
:raise VerificationFailed: when the hash method cannot be found
|
:raise VerificationFailed: when the hash method cannot be found
|
||||||
|
"""
|
||||||
'''
|
|
||||||
|
|
||||||
for (hashname, asn1code) in HASH_ASN1.items():
|
for (hashname, asn1code) in HASH_ASN1.items():
|
||||||
if not method_hash.startswith(asn1code):
|
if asn1code in clearsig:
|
||||||
continue
|
return hashname
|
||||||
|
|
||||||
return (hashname, method_hash[len(asn1code):])
|
|
||||||
|
|
||||||
raise VerificationError('Verification failed')
|
raise VerificationError('Verification failed')
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -14,102 +14,115 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
'''Numerical functions related to primes.
|
"""Numerical functions related to primes.
|
||||||
|
|
||||||
Implementation based on the book Algorithm Design by Michael T. Goodrich and
|
Implementation based on the book Algorithm Design by Michael T. Goodrich and
|
||||||
Roberto Tamassia, 2002.
|
Roberto Tamassia, 2002.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
__all__ = [ 'getprime', 'are_relatively_prime']
|
|
||||||
|
|
||||||
import rsa.randnum
|
import rsa.randnum
|
||||||
|
|
||||||
|
__all__ = ['getprime', 'are_relatively_prime']
|
||||||
|
|
||||||
|
|
||||||
def gcd(p, q):
|
def gcd(p, q):
|
||||||
'''Returns the greatest common divisor of p and q
|
"""Returns the greatest common divisor of p and q
|
||||||
|
|
||||||
>>> gcd(48, 180)
|
>>> gcd(48, 180)
|
||||||
12
|
12
|
||||||
'''
|
"""
|
||||||
|
|
||||||
while q != 0:
|
while q != 0:
|
||||||
if p < q: (p,q) = (q,p)
|
(p, q) = (q, p % q)
|
||||||
(p,q) = (q, p % q)
|
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
def jacobi(a, b):
|
def miller_rabin_primality_testing(n, k):
|
||||||
'''Calculates the value of the Jacobi symbol (a/b) where both a and b are
|
"""Calculates whether n is composite (which is always correct) or prime
|
||||||
positive integers, and b is odd
|
(which theoretically is incorrect with error probability 4**-k), by
|
||||||
|
applying Miller-Rabin primality testing.
|
||||||
|
|
||||||
:returns: -1, 0 or 1
|
For reference and implementation example, see:
|
||||||
'''
|
https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test
|
||||||
|
|
||||||
assert a > 0
|
:param n: Integer to be tested for primality.
|
||||||
assert b > 0
|
: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
|
||||||
|
"""
|
||||||
|
|
||||||
if a == 0: return 0
|
# prevent potential infinite loop when d = 0
|
||||||
result = 1
|
if n < 2:
|
||||||
while a > 1:
|
return False
|
||||||
if a & 1:
|
|
||||||
if ((a-1)*(b-1) >> 2) & 1:
|
|
||||||
result = -result
|
|
||||||
a, b = b % a, a
|
|
||||||
else:
|
|
||||||
if (((b * b) - 1) >> 3) & 1:
|
|
||||||
result = -result
|
|
||||||
a >>= 1
|
|
||||||
if a == 0: return 0
|
|
||||||
return result
|
|
||||||
|
|
||||||
def jacobi_witness(x, n):
|
# Decompose (n - 1) to write it as (2 ** r) * d
|
||||||
'''Returns False if n is an Euler pseudo-prime with base x, and
|
# While d is even, divide it by 2 and increase the exponent.
|
||||||
True otherwise.
|
d = n - 1
|
||||||
'''
|
r = 0
|
||||||
|
|
||||||
j = jacobi(x, n) % n
|
while not (d & 1):
|
||||||
|
r += 1
|
||||||
f = pow(x, n >> 1, n)
|
d >>= 1
|
||||||
|
|
||||||
if j == f: return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def randomized_primality_testing(n, k):
|
|
||||||
'''Calculates whether n is composite (which is always correct) or
|
|
||||||
prime (which is incorrect with error probability 2**-k)
|
|
||||||
|
|
||||||
Returns False if the number is composite, and True if it's
|
|
||||||
probably prime.
|
|
||||||
'''
|
|
||||||
|
|
||||||
# 50% of Jacobi-witnesses can report compositness of non-prime numbers
|
|
||||||
|
|
||||||
# The implemented algorithm using the Jacobi witness function has error
|
|
||||||
# probability q <= 0.5, according to Goodrich et. al
|
|
||||||
#
|
|
||||||
# q = 0.5
|
|
||||||
# t = int(math.ceil(k / log(1 / q, 2)))
|
|
||||||
# So t = k / log(2, 2) = k / 1 = k
|
|
||||||
# this means we can use range(k) rather than range(t)
|
|
||||||
|
|
||||||
|
# Test k witnesses.
|
||||||
for _ in range(k):
|
for _ in range(k):
|
||||||
x = rsa.randnum.randint(n-1)
|
# Generate random integer a, where 2 <= a <= (n - 2)
|
||||||
if jacobi_witness(x, n): return False
|
a = rsa.randnum.randint(n - 4) + 2
|
||||||
|
|
||||||
|
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
|
return True
|
||||||
|
|
||||||
|
|
||||||
def is_prime(number):
|
def is_prime(number):
|
||||||
'''Returns True if the number is prime, and False otherwise.
|
"""Returns True if the number is prime, and False otherwise.
|
||||||
|
|
||||||
|
>>> is_prime(2)
|
||||||
|
True
|
||||||
>>> is_prime(42)
|
>>> is_prime(42)
|
||||||
False
|
False
|
||||||
>>> is_prime(41)
|
>>> is_prime(41)
|
||||||
True
|
True
|
||||||
'''
|
>>> [x for x in range(901, 1000) if is_prime(x)]
|
||||||
|
[907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Check for small numbers.
|
||||||
|
if number < 10:
|
||||||
|
return number in [2, 3, 5, 7]
|
||||||
|
|
||||||
|
# Check for even numbers.
|
||||||
|
if not (number & 1):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 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
|
||||||
|
return miller_rabin_primality_testing(number, 7)
|
||||||
|
|
||||||
return randomized_primality_testing(number, 6)
|
|
||||||
|
|
||||||
def getprime(nbits):
|
def getprime(nbits):
|
||||||
'''Returns a prime number that can be stored in 'nbits' bits.
|
"""Returns a prime number that can be stored in 'nbits' bits.
|
||||||
|
|
||||||
>>> p = getprime(128)
|
>>> p = getprime(128)
|
||||||
>>> is_prime(p-1)
|
>>> is_prime(p-1)
|
||||||
|
@ -122,14 +135,12 @@ def getprime(nbits):
|
||||||
>>> from rsa import common
|
>>> from rsa import common
|
||||||
>>> common.bit_size(p) == 128
|
>>> common.bit_size(p) == 128
|
||||||
True
|
True
|
||||||
|
"""
|
||||||
|
|
||||||
'''
|
assert nbits > 3 # the loop wil hang on too small numbers
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
integer = rsa.randnum.read_random_int(nbits)
|
integer = rsa.randnum.read_random_odd_int(nbits)
|
||||||
|
|
||||||
# Make sure it's odd
|
|
||||||
integer |= 1
|
|
||||||
|
|
||||||
# Test for primeness
|
# Test for primeness
|
||||||
if is_prime(integer):
|
if is_prime(integer):
|
||||||
|
@ -139,17 +150,18 @@ def getprime(nbits):
|
||||||
|
|
||||||
|
|
||||||
def are_relatively_prime(a, b):
|
def are_relatively_prime(a, b):
|
||||||
'''Returns True if a and b are relatively prime, and False if they
|
"""Returns True if a and b are relatively prime, and False if they
|
||||||
are not.
|
are not.
|
||||||
|
|
||||||
>>> are_relatively_prime(2, 3)
|
>>> are_relatively_prime(2, 3)
|
||||||
1
|
True
|
||||||
>>> are_relatively_prime(2, 4)
|
>>> are_relatively_prime(2, 4)
|
||||||
0
|
False
|
||||||
'''
|
"""
|
||||||
|
|
||||||
d = gcd(a, b)
|
d = gcd(a, b)
|
||||||
return (d == 1)
|
return d == 1
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print('Running doctests 1000x or until failure')
|
print('Running doctests 1000x or until failure')
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
'''Functions for generating random numbers.'''
|
"""Functions for generating random numbers."""
|
||||||
|
|
||||||
# Source inspired by code by Yesudeep Mangalapilly <yesudeep@gmail.com>
|
# Source inspired by code by Yesudeep Mangalapilly <yesudeep@gmail.com>
|
||||||
|
|
||||||
|
@ -23,12 +23,13 @@ import os
|
||||||
from rsa import common, transform
|
from rsa import common, transform
|
||||||
from rsa._compat import byte
|
from rsa._compat import byte
|
||||||
|
|
||||||
|
|
||||||
def read_random_bits(nbits):
|
def read_random_bits(nbits):
|
||||||
'''Reads 'nbits' random bits.
|
"""Reads 'nbits' random bits.
|
||||||
|
|
||||||
If nbits isn't a whole number of bytes, an extra byte will be appended with
|
If nbits isn't a whole number of bytes, an extra byte will be appended with
|
||||||
only the lower bits set.
|
only the lower bits set.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
nbytes, rbits = divmod(nbits, 8)
|
nbytes, rbits = divmod(nbits, 8)
|
||||||
|
|
||||||
|
@ -45,8 +46,8 @@ def read_random_bits(nbits):
|
||||||
|
|
||||||
|
|
||||||
def read_random_int(nbits):
|
def read_random_int(nbits):
|
||||||
'''Reads a random integer of approximately nbits bits.
|
"""Reads a random integer of approximately nbits bits.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
randomdata = read_random_bits(nbits)
|
randomdata = read_random_bits(nbits)
|
||||||
value = transform.bytes2int(randomdata)
|
value = transform.bytes2int(randomdata)
|
||||||
|
@ -57,13 +58,27 @@ def read_random_int(nbits):
|
||||||
|
|
||||||
return value
|
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):
|
def randint(maxvalue):
|
||||||
'''Returns a random integer x with 1 <= x <= maxvalue
|
"""Returns a random integer x with 1 <= x <= maxvalue
|
||||||
|
|
||||||
May take a very long time in specific situations. If maxvalue needs N bits
|
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
|
to store, the closer maxvalue is to (2 ** N) - 1, the faster this function
|
||||||
is.
|
is.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
bit_size = common.bit_size(maxvalue)
|
bit_size = common.bit_size(maxvalue)
|
||||||
|
|
||||||
|
@ -81,5 +96,3 @@ def randint(maxvalue):
|
||||||
tries += 1
|
tries += 1
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -14,10 +14,10 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
'''Data transformation functions.
|
"""Data transformation functions.
|
||||||
|
|
||||||
From bytes to a number, number to bytes, etc.
|
From bytes to a number, number to bytes, etc.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ try:
|
||||||
# Using psyco (if available) cuts down the execution time on Python 2.5
|
# Using psyco (if available) cuts down the execution time on Python 2.5
|
||||||
# at least by half.
|
# at least by half.
|
||||||
import psyco
|
import psyco
|
||||||
|
|
||||||
psyco.full()
|
psyco.full()
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
@ -37,32 +38,32 @@ from rsa._compat import is_integer, b, byte, get_word_alignment, ZERO_BYTE, EMPT
|
||||||
|
|
||||||
|
|
||||||
def bytes2int(raw_bytes):
|
def bytes2int(raw_bytes):
|
||||||
r'''Converts a list of bytes or an 8-bit string to an integer.
|
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.
|
When using unicode strings, encode it to some encoding like UTF8 first.
|
||||||
|
|
||||||
>>> (((128 * 256) + 64) * 256) + 15
|
>>> (((128 * 256) + 64) * 256) + 15
|
||||||
8405007
|
8405007
|
||||||
>>> bytes2int('\x80@\x0f')
|
>>> bytes2int(b'\x80@\x0f')
|
||||||
8405007
|
8405007
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return int(binascii.hexlify(raw_bytes), 16)
|
return int(binascii.hexlify(raw_bytes), 16)
|
||||||
|
|
||||||
|
|
||||||
def _int2bytes(number, block_size=None):
|
def _int2bytes(number, block_size=None):
|
||||||
r'''Converts a number to a string of bytes.
|
r"""Converts a number to a string of bytes.
|
||||||
|
|
||||||
Usage::
|
Usage::
|
||||||
|
|
||||||
>>> _int2bytes(123456789)
|
>>> _int2bytes(123456789)
|
||||||
'\x07[\xcd\x15'
|
b'\x07[\xcd\x15'
|
||||||
>>> bytes2int(_int2bytes(123456789))
|
>>> bytes2int(_int2bytes(123456789))
|
||||||
123456789
|
123456789
|
||||||
|
|
||||||
>>> _int2bytes(123456789, 6)
|
>>> _int2bytes(123456789, 6)
|
||||||
'\x00\x00\x07[\xcd\x15'
|
b'\x00\x00\x07[\xcd\x15'
|
||||||
>>> bytes2int(_int2bytes(123456789, 128))
|
>>> bytes2int(_int2bytes(123456789, 128))
|
||||||
123456789
|
123456789
|
||||||
|
|
||||||
|
@ -78,7 +79,8 @@ def _int2bytes(number, block_size=None):
|
||||||
|
|
||||||
@throws OverflowError when block_size is given and the number takes up more
|
@throws OverflowError when block_size is given and the number takes up more
|
||||||
bytes than fit into the block.
|
bytes than fit into the block.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
# Type checking
|
# Type checking
|
||||||
if not is_integer(number):
|
if not is_integer(number):
|
||||||
raise TypeError("You must pass an integer for 'number', not %s" %
|
raise TypeError("You must pass an integer for 'number', not %s" %
|
||||||
|
@ -116,7 +118,7 @@ def _int2bytes(number, block_size=None):
|
||||||
|
|
||||||
|
|
||||||
def bytes_leading(raw_bytes, needle=ZERO_BYTE):
|
def bytes_leading(raw_bytes, needle=ZERO_BYTE):
|
||||||
'''
|
"""
|
||||||
Finds the number of prefixed byte occurrences in the haystack.
|
Finds the number of prefixed byte occurrences in the haystack.
|
||||||
|
|
||||||
Useful when you want to deal with padding.
|
Useful when you want to deal with padding.
|
||||||
|
@ -127,7 +129,8 @@ def bytes_leading(raw_bytes, needle=ZERO_BYTE):
|
||||||
The byte to count. Default \000.
|
The byte to count. Default \000.
|
||||||
:returns:
|
:returns:
|
||||||
The number of leading needle bytes.
|
The number of leading needle bytes.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
leading = 0
|
leading = 0
|
||||||
# Indexing keeps compatibility between Python 2.x and Python 3.x
|
# Indexing keeps compatibility between Python 2.x and Python 3.x
|
||||||
_byte = needle[0]
|
_byte = needle[0]
|
||||||
|
@ -140,7 +143,7 @@ def bytes_leading(raw_bytes, needle=ZERO_BYTE):
|
||||||
|
|
||||||
|
|
||||||
def int2bytes(number, fill_size=None, chunk_size=None, overflow=False):
|
def int2bytes(number, fill_size=None, chunk_size=None, overflow=False):
|
||||||
'''
|
"""
|
||||||
Convert an unsigned integer to bytes (base-256 representation)::
|
Convert an unsigned integer to bytes (base-256 representation)::
|
||||||
|
|
||||||
Does not preserve leading zeros if you don't specify a chunk size or
|
Does not preserve leading zeros if you don't specify a chunk size or
|
||||||
|
@ -172,7 +175,8 @@ def int2bytes(number, fill_size=None, chunk_size=None, overflow=False):
|
||||||
bytes than fit into the block. This requires the ``overflow``
|
bytes than fit into the block. This requires the ``overflow``
|
||||||
argument to this function to be set to ``False`` otherwise, no
|
argument to this function to be set to ``False`` otherwise, no
|
||||||
error will be raised.
|
error will be raised.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if number < 0:
|
if number < 0:
|
||||||
raise ValueError("Number must be an unsigned integer: %d" % number)
|
raise ValueError("Number must be an unsigned integer: %d" % number)
|
||||||
|
|
||||||
|
@ -216,5 +220,5 @@ def int2bytes(number, fill_size=None, chunk_size=None, overflow=False):
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import doctest
|
import doctest
|
||||||
doctest.testmod()
|
|
||||||
|
|
||||||
|
doctest.testmod()
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
'''Utility functions.'''
|
"""Utility functions."""
|
||||||
|
|
||||||
from __future__ import with_statement, print_function
|
from __future__ import with_statement, print_function
|
||||||
|
|
||||||
|
@ -23,8 +23,9 @@ from optparse import OptionParser
|
||||||
|
|
||||||
import rsa.key
|
import rsa.key
|
||||||
|
|
||||||
|
|
||||||
def private_to_public():
|
def private_to_public():
|
||||||
'''Reads a private key and outputs the corresponding public key.'''
|
"""Reads a private key and outputs the corresponding public key."""
|
||||||
|
|
||||||
# Parse the CLI options
|
# Parse the CLI options
|
||||||
parser = OptionParser(usage='usage: %prog [options]',
|
parser = OptionParser(usage='usage: %prog [options]',
|
||||||
|
@ -49,7 +50,7 @@ def private_to_public():
|
||||||
|
|
||||||
# Read the input data
|
# Read the input data
|
||||||
if cli.infilename:
|
if cli.infilename:
|
||||||
print('Reading private key from %s in %s format' % \
|
print('Reading private key from %s in %s format' %
|
||||||
(cli.infilename, cli.inform), file=sys.stderr)
|
(cli.infilename, cli.inform), file=sys.stderr)
|
||||||
with open(cli.infilename, 'rb') as infile:
|
with open(cli.infilename, 'rb') as infile:
|
||||||
in_data = infile.read()
|
in_data = infile.read()
|
||||||
|
@ -60,7 +61,6 @@ def private_to_public():
|
||||||
|
|
||||||
assert type(in_data) == bytes, type(in_data)
|
assert type(in_data) == bytes, type(in_data)
|
||||||
|
|
||||||
|
|
||||||
# Take the public fields and create a public key
|
# Take the public fields and create a public key
|
||||||
priv_key = rsa.key.PrivateKey.load_pkcs1(in_data, cli.inform)
|
priv_key = rsa.key.PrivateKey.load_pkcs1(in_data, cli.inform)
|
||||||
pub_key = rsa.key.PublicKey(priv_key.n, priv_key.e)
|
pub_key = rsa.key.PublicKey(priv_key.n, priv_key.e)
|
||||||
|
@ -69,7 +69,7 @@ def private_to_public():
|
||||||
out_data = pub_key.save_pkcs1(cli.outform)
|
out_data = pub_key.save_pkcs1(cli.outform)
|
||||||
|
|
||||||
if cli.outfilename:
|
if cli.outfilename:
|
||||||
print('Writing public key to %s in %s format' % \
|
print('Writing public key to %s in %s format' %
|
||||||
(cli.outfilename, cli.outform), file=sys.stderr)
|
(cli.outfilename, cli.outform), file=sys.stderr)
|
||||||
with open(cli.outfilename, 'wb') as outfile:
|
with open(cli.outfilename, 'wb') as outfile:
|
||||||
outfile.write(out_data)
|
outfile.write(out_data)
|
||||||
|
@ -77,5 +77,3 @@ def private_to_public():
|
||||||
print('Writing public key to stdout in %s format' % cli.outform,
|
print('Writing public key to stdout in %s format' % cli.outform,
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
sys.stdout.write(out_data.decode('ascii'))
|
sys.stdout.write(out_data.decode('ascii'))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -14,7 +14,25 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
'''VARBLOCK file support
|
"""VARBLOCK file support
|
||||||
|
|
||||||
|
.. deprecated:: 3.4
|
||||||
|
|
||||||
|
The VARBLOCK format is NOT recommended for general use, has been deprecated since
|
||||||
|
Python-RSA 3.4, and will be removed in a future release. It's vulnerable to a
|
||||||
|
number of attacks:
|
||||||
|
|
||||||
|
1. decrypt/encrypt_bigfile() does not implement `Authenticated encryption`_ nor
|
||||||
|
uses MACs to verify messages before decrypting public key encrypted messages.
|
||||||
|
|
||||||
|
2. decrypt/encrypt_bigfile() does not use hybrid encryption (it uses plain RSA)
|
||||||
|
and has no method for chaining, so block reordering is possible.
|
||||||
|
|
||||||
|
See `issue #19 on Github`_ for more information.
|
||||||
|
|
||||||
|
.. _Authenticated encryption: https://en.wikipedia.org/wiki/Authenticated_encryption
|
||||||
|
.. _issue #19 on Github: https://github.com/sybrenstuvel/python-rsa/issues/13
|
||||||
|
|
||||||
|
|
||||||
The VARBLOCK file format is as follows, where || denotes byte concatenation:
|
The VARBLOCK file format is as follows, where || denotes byte concatenation:
|
||||||
|
|
||||||
|
@ -31,25 +49,32 @@ The VARBLOCK file format is as follows, where || denotes byte concatenation:
|
||||||
This file format is called the VARBLOCK format, in line with the varint format
|
This file format is called the VARBLOCK format, in line with the varint format
|
||||||
used to denote the block sizes.
|
used to denote the block sizes.
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
import warnings
|
||||||
|
|
||||||
from rsa._compat import byte, b
|
from rsa._compat import byte, b
|
||||||
|
|
||||||
|
|
||||||
ZERO_BYTE = b('\x00')
|
ZERO_BYTE = b('\x00')
|
||||||
VARBLOCK_VERSION = 1
|
VARBLOCK_VERSION = 1
|
||||||
|
|
||||||
|
warnings.warn("The 'rsa.varblock' module was deprecated in Python-RSA version "
|
||||||
|
"3.4 due to security issues in the VARBLOCK format. See "
|
||||||
|
"https://github.com/sybrenstuvel/python-rsa/issues/13 for more information.",
|
||||||
|
DeprecationWarning)
|
||||||
|
|
||||||
|
|
||||||
def read_varint(infile):
|
def read_varint(infile):
|
||||||
'''Reads a varint from the file.
|
"""Reads a varint from the file.
|
||||||
|
|
||||||
When the first byte to be read indicates EOF, (0, 0) is returned. When an
|
When the first byte to be read indicates EOF, (0, 0) is returned. When an
|
||||||
EOF occurs when at least one byte has been read, an EOFError exception is
|
EOF occurs when at least one byte has been read, an EOFError exception is
|
||||||
raised.
|
raised.
|
||||||
|
|
||||||
@param infile: the file-like object to read from. It should have a read()
|
:param infile: the file-like object to read from. It should have a read()
|
||||||
method.
|
method.
|
||||||
@returns (varint, length), the read varint and the number of read bytes.
|
:returns: (varint, length), the read varint and the number of read bytes.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
varint = 0
|
varint = 0
|
||||||
read_bytes = 0
|
read_bytes = 0
|
||||||
|
@ -58,7 +83,7 @@ def read_varint(infile):
|
||||||
char = infile.read(1)
|
char = infile.read(1)
|
||||||
if len(char) == 0:
|
if len(char) == 0:
|
||||||
if read_bytes == 0:
|
if read_bytes == 0:
|
||||||
return (0, 0)
|
return 0, 0
|
||||||
raise EOFError('EOF while reading varint, value is %i so far' %
|
raise EOFError('EOF while reading varint, value is %i so far' %
|
||||||
varint)
|
varint)
|
||||||
|
|
||||||
|
@ -68,16 +93,16 @@ def read_varint(infile):
|
||||||
read_bytes += 1
|
read_bytes += 1
|
||||||
|
|
||||||
if not byte & 0x80:
|
if not byte & 0x80:
|
||||||
return (varint, read_bytes)
|
return varint, read_bytes
|
||||||
|
|
||||||
|
|
||||||
def write_varint(outfile, value):
|
def write_varint(outfile, value):
|
||||||
'''Writes a varint to a file.
|
"""Writes a varint to a file.
|
||||||
|
|
||||||
@param outfile: the file-like object to write to. It should have a write()
|
:param outfile: the file-like object to write to. It should have a write()
|
||||||
method.
|
method.
|
||||||
@returns the number of written bytes.
|
:returns: the number of written bytes.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
# there is a big difference between 'write the value 0' (this case) and
|
# there is a big difference between 'write the value 0' (this case) and
|
||||||
# 'there is nothing left to write' (the false-case of the while loop)
|
# 'there is nothing left to write' (the false-case of the while loop)
|
||||||
|
@ -89,7 +114,7 @@ def write_varint(outfile, value):
|
||||||
written_bytes = 0
|
written_bytes = 0
|
||||||
while value > 0:
|
while value > 0:
|
||||||
to_write = value & 0x7f
|
to_write = value & 0x7f
|
||||||
value = value >> 7
|
value >>= 7
|
||||||
|
|
||||||
if value > 0:
|
if value > 0:
|
||||||
to_write |= 0x80
|
to_write |= 0x80
|
||||||
|
@ -101,12 +126,12 @@ def write_varint(outfile, value):
|
||||||
|
|
||||||
|
|
||||||
def yield_varblocks(infile):
|
def yield_varblocks(infile):
|
||||||
'''Generator, yields each block in the input file.
|
"""Generator, yields each block in the input file.
|
||||||
|
|
||||||
@param infile: file to read, is expected to have the VARBLOCK format as
|
:param infile: file to read, is expected to have the VARBLOCK format as
|
||||||
described in the module's docstring.
|
described in the module's docstring.
|
||||||
@yields the contents of each block.
|
@yields the contents of each block.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
# Check the version number
|
# Check the version number
|
||||||
first_char = infile.read(1)
|
first_char = infile.read(1)
|
||||||
|
@ -135,11 +160,11 @@ def yield_varblocks(infile):
|
||||||
|
|
||||||
|
|
||||||
def yield_fixedblocks(infile, blocksize):
|
def yield_fixedblocks(infile, blocksize):
|
||||||
'''Generator, yields each block of ``blocksize`` bytes in the input file.
|
"""Generator, yields each block of ``blocksize`` bytes in the input file.
|
||||||
|
|
||||||
:param infile: file to read and separate in blocks.
|
:param infile: file to read and separate in blocks.
|
||||||
:returns: a generator that yields the contents of each block
|
:returns: a generator that yields the contents of each block
|
||||||
'''
|
"""
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
block = infile.read(blocksize)
|
block = infile.read(blocksize)
|
||||||
|
@ -152,4 +177,3 @@ def yield_fixedblocks(infile, blocksize):
|
||||||
|
|
||||||
if read_bytes < blocksize:
|
if read_bytes < blocksize:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue