Steven D'Aprano | 9570272 | 2016-04-15 01:51:31 +1000 | [diff] [blame^] | 1 | """Generate cryptographically strong pseudo-random numbers suitable for |
| 2 | managing secrets such as account authentication, tokens, and similar. |
| 3 | See PEP 506 for more information. |
| 4 | |
| 5 | https://www.python.org/dev/peps/pep-0506/ |
| 6 | |
| 7 | |
| 8 | Random numbers |
| 9 | ============== |
| 10 | |
| 11 | The ``secrets`` module provides the following pseudo-random functions, based |
| 12 | on SystemRandom, which in turn uses the most secure source of randomness your |
| 13 | operating system provides. |
| 14 | |
| 15 | |
| 16 | choice(sequence) |
| 17 | Choose a random element from a non-empty sequence. |
| 18 | |
| 19 | randbelow(n) |
| 20 | Return a random int in the range [0, n). |
| 21 | |
| 22 | randbits(k) |
| 23 | Generates an int with k random bits. |
| 24 | |
| 25 | SystemRandom |
| 26 | Class for generating random numbers using sources provided by |
| 27 | the operating system. See the ``random`` module for documentation. |
| 28 | |
| 29 | |
| 30 | Token functions |
| 31 | =============== |
| 32 | |
| 33 | The ``secrets`` module provides a number of functions for generating secure |
| 34 | tokens, suitable for applications such as password resets, hard-to-guess |
| 35 | URLs, and similar. All the ``token_*`` functions take an optional single |
| 36 | argument specifying the number of bytes of randomness to use. If that is |
| 37 | not given, or is ``None``, a reasonable default is used. That default is |
| 38 | subject to change at any time, including during maintenance releases. |
| 39 | |
| 40 | |
| 41 | token_bytes(nbytes=None) |
| 42 | Return a random byte-string containing ``nbytes`` number of bytes. |
| 43 | |
| 44 | >>> secrets.token_bytes(16) #doctest:+SKIP |
| 45 | b'\\xebr\\x17D*t\\xae\\xd4\\xe3S\\xb6\\xe2\\xebP1\\x8b' |
| 46 | |
| 47 | |
| 48 | token_hex(nbytes=None) |
| 49 | Return a random text-string, in hexadecimal. The string has ``nbytes`` |
| 50 | random bytes, each byte converted to two hex digits. |
| 51 | |
| 52 | >>> secrets.token_hex(16) #doctest:+SKIP |
| 53 | 'f9bf78b9a18ce6d46a0cd2b0b86df9da' |
| 54 | |
| 55 | token_urlsafe(nbytes=None) |
| 56 | Return a random URL-safe text-string, containing ``nbytes`` random |
| 57 | bytes. On average, each byte results in approximately 1.3 characters |
| 58 | in the final result. |
| 59 | |
| 60 | >>> secrets.token_urlsafe(16) #doctest:+SKIP |
| 61 | 'Drmhze6EPcv0fN_81Bj-nA' |
| 62 | |
| 63 | |
| 64 | (The examples above assume Python 3. In Python 2, byte-strings will display |
| 65 | using regular quotes ``''`` with no prefix, and text-strings will have a |
| 66 | ``u`` prefix.) |
| 67 | |
| 68 | |
| 69 | Other functions |
| 70 | =============== |
| 71 | |
| 72 | compare_digest(a, b) |
| 73 | Return True if strings a and b are equal, otherwise False. |
| 74 | Performs the equality comparison in such a way as to reduce the |
| 75 | risk of timing attacks. |
| 76 | |
| 77 | See http://codahale.com/a-lesson-in-timing-attacks/ for a |
| 78 | discussion on how timing attacks against ``==`` can reveal |
| 79 | secrets from your application. |
| 80 | |
| 81 | |
| 82 | """ |
| 83 | |
| 84 | __all__ = ['choice', 'randbelow', 'randbits', 'SystemRandom', |
| 85 | 'token_bytes', 'token_hex', 'token_urlsafe', |
| 86 | 'compare_digest', |
| 87 | ] |
| 88 | |
| 89 | |
| 90 | import base64 |
| 91 | import binascii |
| 92 | import os |
| 93 | |
| 94 | try: |
| 95 | from hmac import compare_digest |
| 96 | except ImportError: |
| 97 | # Python version is too old. Fall back to a pure-Python version. |
| 98 | |
| 99 | import operator |
| 100 | from functools import reduce |
| 101 | |
| 102 | def compare_digest(a, b): |
| 103 | """Return ``a == b`` using an approach resistant to timing analysis. |
| 104 | |
| 105 | a and b must both be of the same type: either both text strings, |
| 106 | or both byte strings. |
| 107 | |
| 108 | Note: If a and b are of different lengths, or if an error occurs, |
| 109 | a timing attack could theoretically reveal information about the |
| 110 | types and lengths of a and b, but not their values. |
| 111 | """ |
| 112 | # For a similar approach, see |
| 113 | # http://codahale.com/a-lesson-in-timing-attacks/ |
| 114 | for T in (bytes, str): |
| 115 | if isinstance(a, T) and isinstance(b, T): |
| 116 | break |
| 117 | else: # for...else |
| 118 | raise TypeError("arguments must be both strings or both bytes") |
| 119 | if len(a) != len(b): |
| 120 | return False |
| 121 | # Thanks to Raymond Hettinger for this one-liner. |
| 122 | return reduce(operator.and_, map(operator.eq, a, b), True) |
| 123 | |
| 124 | |
| 125 | |
| 126 | from random import SystemRandom |
| 127 | |
| 128 | _sysrand = SystemRandom() |
| 129 | |
| 130 | randbits = _sysrand.getrandbits |
| 131 | choice = _sysrand.choice |
| 132 | |
| 133 | def randbelow(exclusive_upper_bound): |
| 134 | return _sysrand._randbelow(exclusive_upper_bound) |
| 135 | |
| 136 | DEFAULT_ENTROPY = 32 # number of bytes to return by default |
| 137 | |
| 138 | def token_bytes(nbytes=None): |
| 139 | if nbytes is None: |
| 140 | nbytes = DEFAULT_ENTROPY |
| 141 | return os.urandom(nbytes) |
| 142 | |
| 143 | def token_hex(nbytes=None): |
| 144 | return binascii.hexlify(token_bytes(nbytes)).decode('ascii') |
| 145 | |
| 146 | def token_urlsafe(nbytes=None): |
| 147 | tok = token_bytes(nbytes) |
| 148 | return base64.urlsafe_b64encode(tok).rstrip(b'=').decode('ascii') |
| 149 | |