Source code for tangled.util.random

import binascii
import hmac
import os
import random
import string


__all__ = ['constant_time_compare', 'random_bytes', 'random_string']


random = random.SystemRandom()


ASCII_ALPHANUMERIC = string.ascii_letters + string.digits
BASE64_ALPHABET = ASCII_ALPHANUMERIC + '-_'
HEX = string.digits + 'abcdef'


[docs]def random_bytes(n=16, as_hex=True): """Return a random string of bytes. By default, this will encode 16 random bytes as a 32-character byte string of hex digits (i.e., each byte is split into 4 bits and encoded as a hex digit). In general, whenever ``as_hex`` is True, the number of bytes returned will be ``2 * n``. >>> len(random_bytes()) == 32 True >>> len(random_bytes(10, as_hex=True)) == 20 True >>> len(random_bytes(7, as_hex=False)) == 7 True >>> random_bytes().__class__ is bytes True >>> random_bytes(as_hex=False).__class__ is bytes True """ _bytes = os.urandom(n) if as_hex: return binascii.hexlify(_bytes) else: return _bytes
[docs]def random_string(n=32, alphabet=BASE64_ALPHABET, encoding='ascii') -> str: """Return a random string with length ``n``. By default, the string will contain 32 characters from the URL-safe base 64 alphabet. ``encoding`` is used only if the ``alphabet`` is a byte string. >>> len(random_string()) == 32 True >>> len(random_string(8)) == 8 True >>> len(random_string(7, ASCII_ALPHANUMERIC)) == 7 True >>> random_string().__class__ is str True >>> random_string(alphabet=HEX).__class__ is str True >>> 'g' not in random_string(alphabet=HEX) True """ a = alphabet[0] chars = (random.choice(alphabet) for _ in range(n)) if isinstance(a, str): return ''.join(chars) elif isinstance(a, bytes): return b''.join(chars).decode(encoding) raise TypeError('Expected str or bytes; got %s' % a.__class__)
[docs]def constant_time_compare(a, b): """Compare two bytes or str objects in constant time. ``a`` and ``b`` must be either both bytes OR both strings w/ only ASCII chars. Returns ``False`` if ``a`` and ``b`` have different lengths, if either is a string with non-ASCII characters, or their types don't match. See :func:`hmac.compare_digest` for more details. """ if len(a) != len(b): return False try: return hmac.compare_digest(a, b) except TypeError: return False