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