(root)/
Python-3.12.0/
Lib/
crypt.py
       1  """Wrapper to the POSIX crypt library call and associated functionality."""
       2  
       3  import sys as _sys
       4  
       5  try:
       6      import _crypt
       7  except ModuleNotFoundError:
       8      if _sys.platform == 'win32':
       9          raise ImportError("The crypt module is not supported on Windows")
      10      else:
      11          raise ImportError("The required _crypt module was not built as part of CPython")
      12  
      13  import errno
      14  import string as _string
      15  import warnings
      16  from random import SystemRandom as _SystemRandom
      17  from collections import namedtuple as _namedtuple
      18  
      19  
      20  warnings._deprecated(__name__, remove=(3, 13))
      21  
      22  
      23  _saltchars = _string.ascii_letters + _string.digits + './'
      24  _sr = _SystemRandom()
      25  
      26  
      27  class ESC[4;38;5;81m_Method(ESC[4;38;5;149m_namedtuple('_Method', 'name ident salt_chars total_size')):
      28  
      29      """Class representing a salt method per the Modular Crypt Format or the
      30      legacy 2-character crypt method."""
      31  
      32      def __repr__(self):
      33          return '<crypt.METHOD_{}>'.format(self.name)
      34  
      35  
      36  def mksalt(method=None, *, rounds=None):
      37      """Generate a salt for the specified method.
      38  
      39      If not specified, the strongest available method will be used.
      40  
      41      """
      42      if method is None:
      43          method = methods[0]
      44      if rounds is not None and not isinstance(rounds, int):
      45          raise TypeError(f'{rounds.__class__.__name__} object cannot be '
      46                          f'interpreted as an integer')
      47      if not method.ident:  # traditional
      48          s = ''
      49      else:  # modular
      50          s = f'${method.ident}$'
      51  
      52      if method.ident and method.ident[0] == '2':  # Blowfish variants
      53          if rounds is None:
      54              log_rounds = 12
      55          else:
      56              log_rounds = int.bit_length(rounds-1)
      57              if rounds != 1 << log_rounds:
      58                  raise ValueError('rounds must be a power of 2')
      59              if not 4 <= log_rounds <= 31:
      60                  raise ValueError('rounds out of the range 2**4 to 2**31')
      61          s += f'{log_rounds:02d}$'
      62      elif method.ident in ('5', '6'):  # SHA-2
      63          if rounds is not None:
      64              if not 1000 <= rounds <= 999_999_999:
      65                  raise ValueError('rounds out of the range 1000 to 999_999_999')
      66              s += f'rounds={rounds}$'
      67      elif rounds is not None:
      68          raise ValueError(f"{method} doesn't support the rounds argument")
      69  
      70      s += ''.join(_sr.choice(_saltchars) for char in range(method.salt_chars))
      71      return s
      72  
      73  
      74  def crypt(word, salt=None):
      75      """Return a string representing the one-way hash of a password, with a salt
      76      prepended.
      77  
      78      If ``salt`` is not specified or is ``None``, the strongest
      79      available method will be selected and a salt generated.  Otherwise,
      80      ``salt`` may be one of the ``crypt.METHOD_*`` values, or a string as
      81      returned by ``crypt.mksalt()``.
      82  
      83      """
      84      if salt is None or isinstance(salt, _Method):
      85          salt = mksalt(salt)
      86      return _crypt.crypt(word, salt)
      87  
      88  
      89  #  available salting/crypto methods
      90  methods = []
      91  
      92  def _add_method(name, *args, rounds=None):
      93      method = _Method(name, *args)
      94      globals()['METHOD_' + name] = method
      95      salt = mksalt(method, rounds=rounds)
      96      result = None
      97      try:
      98          result = crypt('', salt)
      99      except OSError as e:
     100          # Not all libc libraries support all encryption methods.
     101          if e.errno in {errno.EINVAL, errno.EPERM, errno.ENOSYS}:
     102              return False
     103          raise
     104      if result and len(result) == method.total_size:
     105          methods.append(method)
     106          return True
     107      return False
     108  
     109  _add_method('SHA512', '6', 16, 106)
     110  _add_method('SHA256', '5', 16, 63)
     111  
     112  # Choose the strongest supported version of Blowfish hashing.
     113  # Early versions have flaws.  Version 'a' fixes flaws of
     114  # the initial implementation, 'b' fixes flaws of 'a'.
     115  # 'y' is the same as 'b', for compatibility
     116  # with openwall crypt_blowfish.
     117  for _v in 'b', 'y', 'a', '':
     118      if _add_method('BLOWFISH', '2' + _v, 22, 59 + len(_v), rounds=1<<4):
     119          break
     120  
     121  _add_method('MD5', '1', 8, 34)
     122  _add_method('CRYPT', None, 2, 13)
     123  
     124  del _v, _add_method