mirror of
				https://github.com/Legrandin/pycryptodome.git
				synced 2025-10-31 05:31:23 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			392 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			392 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # ===================================================================
 | |
| #
 | |
| # Copyright (c) 2018, Helder Eijs <helderijs@gmail.com>
 | |
| # All rights reserved.
 | |
| #
 | |
| # Redistribution and use in source and binary forms, with or without
 | |
| # modification, are permitted provided that the following conditions
 | |
| # are met:
 | |
| #
 | |
| # 1. Redistributions of source code must retain the above copyright
 | |
| #    notice, this list of conditions and the following disclaimer.
 | |
| # 2. Redistributions in binary form must reproduce the above copyright
 | |
| #    notice, this list of conditions and the following disclaimer in
 | |
| #    the documentation and/or other materials provided with the
 | |
| #    distribution.
 | |
| #
 | |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 | |
| # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 | |
| # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 | |
| # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 | |
| # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | |
| # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 | |
| # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | |
| # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 | |
| # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | |
| # POSSIBILITY OF SUCH DAMAGE.
 | |
| # ===================================================================
 | |
| 
 | |
| import abc
 | |
| 
 | |
| from Crypto.Util.py3compat import iter_range, bord, bchr, ABC
 | |
| 
 | |
| from Crypto import Random
 | |
| 
 | |
| 
 | |
| class IntegerBase(ABC):
 | |
| 
 | |
|     # Conversions
 | |
|     @abc.abstractmethod
 | |
|     def __int__(self):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __str__(self):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __repr__(self):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def to_bytes(self, block_size=0):
 | |
|         pass
 | |
| 
 | |
|     @staticmethod
 | |
|     @abc.abstractmethod
 | |
|     def from_bytes(byte_string):
 | |
|         pass
 | |
| 
 | |
|     # Relations
 | |
|     @abc.abstractmethod
 | |
|     def __eq__(self, term):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __ne__(self, term):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __lt__(self, term):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __le__(self, term):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __gt__(self, term):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __ge__(self, term):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __nonzero__(self):
 | |
|         pass
 | |
|     __bool__ = __nonzero__
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def is_negative(self):
 | |
|         pass
 | |
| 
 | |
|     # Arithmetic operations
 | |
|     @abc.abstractmethod
 | |
|     def __add__(self, term):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __sub__(self, term):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __mul__(self, factor):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __floordiv__(self, divisor):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __mod__(self, divisor):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def inplace_pow(self, exponent, modulus=None):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __pow__(self, exponent, modulus=None):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __abs__(self):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def sqrt(self, modulus=None):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __iadd__(self, term):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __isub__(self, term):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __imul__(self, term):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __imod__(self, term):
 | |
|         pass
 | |
| 
 | |
|     # Boolean/bit operations
 | |
|     @abc.abstractmethod
 | |
|     def __and__(self, term):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __or__(self, term):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __rshift__(self, pos):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __irshift__(self, pos):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __lshift__(self, pos):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def __ilshift__(self, pos):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def get_bit(self, n):
 | |
|         pass
 | |
| 
 | |
|     # Extra
 | |
|     @abc.abstractmethod
 | |
|     def is_odd(self):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def is_even(self):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def size_in_bits(self):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def size_in_bytes(self):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def is_perfect_square(self):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def fail_if_divisible_by(self, small_prime):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def multiply_accumulate(self, a, b):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def set(self, source):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def inplace_inverse(self, modulus):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def inverse(self, modulus):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def gcd(self, term):
 | |
|         pass
 | |
| 
 | |
|     @abc.abstractmethod
 | |
|     def lcm(self, term):
 | |
|         pass
 | |
| 
 | |
|     @staticmethod
 | |
|     @abc.abstractmethod
 | |
|     def jacobi_symbol(a, n):
 | |
|         pass
 | |
|     
 | |
|     @staticmethod
 | |
|     def _tonelli_shanks(n, p):
 | |
|         """Tonelli-shanks algorithm for computing the square root
 | |
|         of n modulo a prime p.
 | |
| 
 | |
|         n must be in the range [0..p-1].
 | |
|         p must be at least even.
 | |
| 
 | |
|         The return value r is the square root of modulo p. If non-zero,
 | |
|         another solution will also exist (p-r).
 | |
| 
 | |
|         Note we cannot assume that p is really a prime: if it's not,
 | |
|         we can either raise an exception or return the correct value.
 | |
|         """
 | |
| 
 | |
|         # See https://rosettacode.org/wiki/Tonelli-Shanks_algorithm
 | |
| 
 | |
|         if n in (0, 1):
 | |
|             return n
 | |
| 
 | |
|         if p % 4 == 3:
 | |
|             root = pow(n, (p + 1) // 4, p)
 | |
|             if pow(root, 2, p) != n:
 | |
|                 raise ValueError("Cannot compute square root")
 | |
|             return root
 | |
| 
 | |
|         s = 1
 | |
|         q = (p - 1) // 2
 | |
|         while not (q & 1):
 | |
|             s += 1
 | |
|             q >>= 1
 | |
| 
 | |
|         z = n.__class__(2)
 | |
|         while True:
 | |
|             euler = pow(z, (p - 1) // 2, p)
 | |
|             if euler == 1:
 | |
|                 z += 1
 | |
|                 continue
 | |
|             if euler == p - 1:
 | |
|                 break
 | |
|             # Most probably p is not a prime
 | |
|             raise ValueError("Cannot compute square root")
 | |
| 
 | |
|         m = s
 | |
|         c = pow(z, q, p)
 | |
|         t = pow(n, q, p)
 | |
|         r = pow(n, (q + 1) // 2, p)
 | |
| 
 | |
|         while t != 1:
 | |
|             for i in iter_range(0, m):
 | |
|                 if pow(t, 2**i, p) == 1:
 | |
|                     break
 | |
|             if i == m:
 | |
|                 raise ValueError("Cannot compute square root of %d mod %d" % (n, p))
 | |
|             b = pow(c, 2**(m - i - 1), p)
 | |
|             m = i
 | |
|             c = b**2 % p
 | |
|             t = (t * b**2) % p
 | |
|             r = (r * b) % p
 | |
| 
 | |
|         if pow(r, 2, p) != n:
 | |
|             raise ValueError("Cannot compute square root")
 | |
| 
 | |
|         return r
 | |
| 
 | |
|     @classmethod
 | |
|     def random(cls, **kwargs):
 | |
|         """Generate a random natural integer of a certain size.
 | |
| 
 | |
|         :Keywords:
 | |
|           exact_bits : positive integer
 | |
|             The length in bits of the resulting random Integer number.
 | |
|             The number is guaranteed to fulfil the relation:
 | |
| 
 | |
|                 2^bits > result >= 2^(bits - 1)
 | |
| 
 | |
|           max_bits : positive integer
 | |
|             The maximum length in bits of the resulting random Integer number.
 | |
|             The number is guaranteed to fulfil the relation:
 | |
| 
 | |
|                 2^bits > result >=0
 | |
| 
 | |
|           randfunc : callable
 | |
|             A function that returns a random byte string. The length of the
 | |
|             byte string is passed as parameter. Optional.
 | |
|             If not provided (or ``None``), randomness is read from the system RNG.
 | |
| 
 | |
|         :Return: a Integer object
 | |
|         """
 | |
| 
 | |
|         exact_bits = kwargs.pop("exact_bits", None)
 | |
|         max_bits = kwargs.pop("max_bits", None)
 | |
|         randfunc = kwargs.pop("randfunc", None)
 | |
| 
 | |
|         if randfunc is None:
 | |
|             randfunc = Random.new().read
 | |
| 
 | |
|         if exact_bits is None and max_bits is None:
 | |
|             raise ValueError("Either 'exact_bits' or 'max_bits' must be specified")
 | |
| 
 | |
|         if exact_bits is not None and max_bits is not None:
 | |
|             raise ValueError("'exact_bits' and 'max_bits' are mutually exclusive")
 | |
| 
 | |
|         bits = exact_bits or max_bits
 | |
|         bytes_needed = ((bits - 1) // 8) + 1
 | |
|         significant_bits_msb = 8 - (bytes_needed * 8 - bits)
 | |
|         msb = bord(randfunc(1)[0])
 | |
|         if exact_bits is not None:
 | |
|             msb |= 1 << (significant_bits_msb - 1)
 | |
|         msb &= (1 << significant_bits_msb) - 1
 | |
| 
 | |
|         return cls.from_bytes(bchr(msb) + randfunc(bytes_needed - 1))
 | |
| 
 | |
|     @classmethod
 | |
|     def random_range(cls, **kwargs):
 | |
|         """Generate a random integer within a given internal.
 | |
| 
 | |
|         :Keywords:
 | |
|           min_inclusive : integer
 | |
|             The lower end of the interval (inclusive).
 | |
|           max_inclusive : integer
 | |
|             The higher end of the interval (inclusive).
 | |
|           max_exclusive : integer
 | |
|             The higher end of the interval (exclusive).
 | |
|           randfunc : callable
 | |
|             A function that returns a random byte string. The length of the
 | |
|             byte string is passed as parameter. Optional.
 | |
|             If not provided (or ``None``), randomness is read from the system RNG.
 | |
|         :Returns:
 | |
|             An Integer randomly taken in the given interval.
 | |
|         """
 | |
| 
 | |
|         min_inclusive = kwargs.pop("min_inclusive", None)
 | |
|         max_inclusive = kwargs.pop("max_inclusive", None)
 | |
|         max_exclusive = kwargs.pop("max_exclusive", None)
 | |
|         randfunc = kwargs.pop("randfunc", None)
 | |
| 
 | |
|         if kwargs:
 | |
|             raise ValueError("Unknown keywords: " + str(kwargs.keys))
 | |
|         if None not in (max_inclusive, max_exclusive):
 | |
|             raise ValueError("max_inclusive and max_exclusive cannot be both"
 | |
|                          " specified")
 | |
|         if max_exclusive is not None:
 | |
|             max_inclusive = max_exclusive - 1
 | |
|         if None in (min_inclusive, max_inclusive):
 | |
|             raise ValueError("Missing keyword to identify the interval")
 | |
| 
 | |
|         if randfunc is None:
 | |
|             randfunc = Random.new().read
 | |
| 
 | |
|         norm_maximum = max_inclusive - min_inclusive
 | |
|         bits_needed = cls(norm_maximum).size_in_bits()
 | |
| 
 | |
|         norm_candidate = -1
 | |
|         while not 0 <= norm_candidate <= norm_maximum:
 | |
|             norm_candidate = cls.random(
 | |
|                                     max_bits=bits_needed,
 | |
|                                     randfunc=randfunc
 | |
|                                     )
 | |
|         return norm_candidate + min_inclusive
 | |
| 
 | 
