1 | n/a | """Generate cryptographically strong pseudo-random numbers suitable for |
---|
2 | n/a | managing secrets such as account authentication, tokens, and similar. |
---|
3 | n/a | |
---|
4 | n/a | See PEP 506 for more information. |
---|
5 | n/a | https://www.python.org/dev/peps/pep-0506/ |
---|
6 | n/a | |
---|
7 | n/a | """ |
---|
8 | n/a | |
---|
9 | n/a | __all__ = ['choice', 'randbelow', 'randbits', 'SystemRandom', |
---|
10 | n/a | 'token_bytes', 'token_hex', 'token_urlsafe', |
---|
11 | n/a | 'compare_digest', |
---|
12 | n/a | ] |
---|
13 | n/a | |
---|
14 | n/a | |
---|
15 | n/a | import base64 |
---|
16 | n/a | import binascii |
---|
17 | n/a | import os |
---|
18 | n/a | |
---|
19 | n/a | from hmac import compare_digest |
---|
20 | n/a | from random import SystemRandom |
---|
21 | n/a | |
---|
22 | n/a | _sysrand = SystemRandom() |
---|
23 | n/a | |
---|
24 | n/a | randbits = _sysrand.getrandbits |
---|
25 | n/a | choice = _sysrand.choice |
---|
26 | n/a | |
---|
27 | n/a | def randbelow(exclusive_upper_bound): |
---|
28 | n/a | """Return a random int in the range [0, n).""" |
---|
29 | n/a | if exclusive_upper_bound <= 0: |
---|
30 | n/a | raise ValueError("Upper bound must be positive.") |
---|
31 | n/a | return _sysrand._randbelow(exclusive_upper_bound) |
---|
32 | n/a | |
---|
33 | n/a | DEFAULT_ENTROPY = 32 # number of bytes to return by default |
---|
34 | n/a | |
---|
35 | n/a | def token_bytes(nbytes=None): |
---|
36 | n/a | """Return a random byte string containing *nbytes* bytes. |
---|
37 | n/a | |
---|
38 | n/a | If *nbytes* is ``None`` or not supplied, a reasonable |
---|
39 | n/a | default is used. |
---|
40 | n/a | |
---|
41 | n/a | >>> token_bytes(16) #doctest:+SKIP |
---|
42 | n/a | b'\\xebr\\x17D*t\\xae\\xd4\\xe3S\\xb6\\xe2\\xebP1\\x8b' |
---|
43 | n/a | |
---|
44 | n/a | """ |
---|
45 | n/a | if nbytes is None: |
---|
46 | n/a | nbytes = DEFAULT_ENTROPY |
---|
47 | n/a | return os.urandom(nbytes) |
---|
48 | n/a | |
---|
49 | n/a | def token_hex(nbytes=None): |
---|
50 | n/a | """Return a random text string, in hexadecimal. |
---|
51 | n/a | |
---|
52 | n/a | The string has *nbytes* random bytes, each byte converted to two |
---|
53 | n/a | hex digits. If *nbytes* is ``None`` or not supplied, a reasonable |
---|
54 | n/a | default is used. |
---|
55 | n/a | |
---|
56 | n/a | >>> token_hex(16) #doctest:+SKIP |
---|
57 | n/a | 'f9bf78b9a18ce6d46a0cd2b0b86df9da' |
---|
58 | n/a | |
---|
59 | n/a | """ |
---|
60 | n/a | return binascii.hexlify(token_bytes(nbytes)).decode('ascii') |
---|
61 | n/a | |
---|
62 | n/a | def token_urlsafe(nbytes=None): |
---|
63 | n/a | """Return a random URL-safe text string, in Base64 encoding. |
---|
64 | n/a | |
---|
65 | n/a | The string has *nbytes* random bytes. If *nbytes* is ``None`` |
---|
66 | n/a | or not supplied, a reasonable default is used. |
---|
67 | n/a | |
---|
68 | n/a | >>> token_urlsafe(16) #doctest:+SKIP |
---|
69 | n/a | 'Drmhze6EPcv0fN_81Bj-nA' |
---|
70 | n/a | |
---|
71 | n/a | """ |
---|
72 | n/a | tok = token_bytes(nbytes) |
---|
73 | n/a | return base64.urlsafe_b64encode(tok).rstrip(b'=').decode('ascii') |
---|