├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── setup.py ├── unit_tests.py └── utilitybelt ├── __init__.py ├── base16.py ├── charsets.py ├── dicts.py └── entropy.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # Mr Developer 34 | .mr.developer.cfg 35 | .project 36 | .pydevproject 37 | 38 | ignore -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - "2.7" 5 | 6 | script: python unit_tests.py 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 The OpenName Project 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Utility Belt 2 | ============= 3 | 4 | [![CircleCI](https://img.shields.io/circleci/project/blockstack/python-utilitybelt/master.svg)](https://circleci.com/gh/blockstack/python-utilitybelt/tree/master) 5 | [![PyPI](https://img.shields.io/pypi/v/utilitybelt.svg)](https://pypi.python.org/pypi/utilitybelt/) 6 | [![PyPI](https://img.shields.io/pypi/dm/utilitybelt.svg)](https://pypi.python.org/pypi/utilitybelt/) 7 | [![PyPI](https://img.shields.io/pypi/l/utilitybelt.svg)](https://github.com/onenameio/utilitybelt/blob/master/LICENSE) 8 | 9 | ### Entropy 10 | 11 | ```python 12 | >>> from binascii import hexlify 13 | >>> from utilitybelt import dev_random_entropy, dev_urandom_entropy 14 | >>> hexlify(dev_random_entropy(16)) 15 | 'cc8752461384261063a979bf5d92ad49' 16 | >>> hexlify(dev_urandom_entropy(16)) 17 | '874ac235edfa658bd46c763079acc096' 18 | ``` 19 | 20 | ### Base16 21 | 22 | ```python 23 | >>> from utilitybelt import hex_to_int, int_to_hex, is_hex, is_int 24 | >>> hex_to_int('deadbeef') 25 | 3735928559 26 | >>> int_to_hex(3735928559) 27 | 'deadbeef' 28 | >>> is_hex('deadbeef') 29 | True 30 | >>> is_hex('xdeadbeefx') 31 | False 32 | >>> is_int(123) 33 | True 34 | >>> is_int('deadbeef') 35 | False 36 | ``` 37 | 38 | ### Dicts 39 | 40 | #### Recursive dicts 41 | 42 | ```python 43 | >>> from utilitybelt import recursive_dict, recursive_dict_to_dict 44 | >>> rd = recursive_dict() 45 | >>> rd['a']['b']['c'] = 1 46 | >>> rd['a']['b']['c'] 47 | 1 48 | >>> rd['a']['b']['d'] 49 | defaultdict( at 0x102912b90>, {}) 50 | >>> d = recursive_dict_to_dict(rd); print d 51 | {'a': {'b': {'c': 1, 'd': {}}}} 52 | ``` 53 | 54 | #### Scrubbing dicts 55 | 56 | ```python 57 | >>> from utilitybelt import scrub_dict 58 | >>> d 59 | {'a': {'b': {'c': 1, 'd': {}}}} 60 | >>> scrub_dict(d) 61 | {'a': {'b': {'c': 1}}} 62 | ``` 63 | 64 | #### Converting objects to dicts 65 | 66 | ```python 67 | >>> from utilitybelt import to_dict 68 | >>> class Rectangle(): 69 | >>> def __init__(self, width, length): 70 | >>> self.width = width 71 | >>> self.length = length 72 | >>> rectangle = Rectangle(2, 4) 73 | >>> to_dict(rectangle) 74 | {'width': 2, 'length': 4} 75 | ``` 76 | 77 | ### Charsets 78 | 79 | #### Moving from string charsets to ints and vice versa 80 | 81 | ```python 82 | >>> from utilitybelt import charset_to_int, int_to_charset 83 | >>> charset_to_int('deadbeef', string.hexdigits[0:16]) 84 | 3735928559 85 | >>> int_to_charset(3735928559, string.hexdigits[0:16]) 86 | 'deadbeef' 87 | ``` 88 | 89 | #### Changing arbitrary charsets 90 | 91 | ```python 92 | >>> from utilitybelt import change_charset 93 | >>> change_charset('deadbeef', string.hexdigits[0:16], '01') 94 | '11011110101011011011111011101111' 95 | >>> change_charset('11011110101011011011111011101111', '01', string.hexdigits[0:16]) 96 | 'deadbeef' 97 | ``` 98 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | Useful Utils 3 | ============== 4 | 5 | """ 6 | 7 | from setuptools import setup, find_packages 8 | 9 | setup( 10 | name='utilitybelt', 11 | version='0.2.6', 12 | author='Halfmoon Labs', 13 | author_email='hello@halfmoonlabs.com', 14 | description='Generally useful tools. A python utility belt.', 15 | keywords=('dict dictionary scrub to_dict todict json characters charset ' 16 | 'hex entropy utility'), 17 | url='https://github.com/onenameio/utilitybelt', 18 | license='MIT', 19 | packages=find_packages(), 20 | install_requires=[ 21 | ], 22 | classifiers=[ 23 | 'Intended Audience :: Developers', 24 | 'License :: OSI Approved :: MIT License', 25 | 'Operating System :: OS Independent', 26 | 'Programming Language :: Python', 27 | ], 28 | zip_safe=False, 29 | ) 30 | -------------------------------------------------------------------------------- /unit_tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Utilitybelt 4 | ~~~~~ 5 | 6 | :copyright: (c) 2015 by Halfmoon Labs 7 | :license: MIT, see LICENSE for more details. 8 | """ 9 | 10 | import unittest 11 | from test import test_support 12 | 13 | import os 14 | import sys 15 | import string 16 | import binascii 17 | from base64 import b64encode, b64decode 18 | 19 | from utilitybelt import recursive_dict, scrub_dict, to_dict, \ 20 | recursive_dict_to_dict 21 | from utilitybelt import int_to_charset, charset_to_int, change_charset, \ 22 | base16_chars, base58_chars, base32_chars, zbase32_chars, base64_chars 23 | from utilitybelt import hex_to_int, int_to_hex, hex_to_charset, \ 24 | charset_to_hex, hexpad, is_hex, is_int, is_valid_int 25 | from utilitybelt import dev_urandom_entropy, dev_random_entropy, secure_randint 26 | 27 | 28 | class IntToCharsetTests(unittest.TestCase): 29 | def setUp(self): 30 | pass 31 | 32 | def tearDown(self): 33 | pass 34 | 35 | def test_int_to_deadbeef(self): 36 | i = 3735928559 37 | reference_value = ("%x" % i).replace('0x', '') 38 | value = int_to_charset(i, base16_chars) 39 | self.assertEqual(value, reference_value) 40 | 41 | def test_long_to_hex(self): 42 | i = int("985936198705846800453632448571546073741637651923621735932" 43 | "00564555477131708560") 44 | reference_value = hex(i).rstrip('L').lstrip('0x') 45 | value = int_to_charset(i, base16_chars) 46 | self.assertEqual(value, reference_value) 47 | 48 | 49 | class CharsetToIntTests(unittest.TestCase): 50 | def setUp(self): 51 | pass 52 | 53 | def tearDown(self): 54 | pass 55 | 56 | def test_deadbeef_to_int(self): 57 | s = "deadbeef" 58 | value = charset_to_int(s, base16_chars) 59 | reference_value = int(s, 16) 60 | self.assertEqual(value, reference_value) 61 | 62 | 63 | class Base16Tests(unittest.TestCase): 64 | def setUp(self): 65 | pass 66 | 67 | def tearDown(self): 68 | pass 69 | 70 | def test_int_to_hex_with_long(self): 71 | i = int("985936198705846800453632448571546073741637651923621735932" 72 | "00564555477131708560") 73 | reference_value = hex(i) 74 | reference_value = reference_value.rstrip('L').lstrip('0x') 75 | value = int_to_hex(i) 76 | self.assertEqual(value, reference_value) 77 | 78 | def test_hex_to_int_with_64bit_string(self): 79 | s = "d9fa02e46cd3867f51279dfae592d3706022ee93c175b49c30c8c962722fc890" 80 | reference_value = int(s, 16) 81 | value = hex_to_int(s) 82 | self.assertEqual(value, reference_value) 83 | 84 | def test_is_int_with_long(self): 85 | i = int("985936198705846800453632448571546073741637651923621735932" 86 | "00564555477131708560") 87 | self.assertTrue(is_int(i)) 88 | 89 | def test_is_valid_int_with_string(self): 90 | i = ("9859361987058468004536324485715460737416376519236217359320056" 91 | "4555477131708560") 92 | self.assertTrue(is_valid_int(i)) 93 | 94 | def test_is_hex_with_64bit_string(self): 95 | s = "d9fa02e46cd3867f51279dfae592d3706022ee93c175b49c30c8c962722fc890" 96 | self.assertTrue(is_hex(s)) 97 | 98 | def test_hex_to_charset(self): 99 | s = "d9fa02e46cd3867f51279dfae592d3706022ee93c175b49c30c8c962722fc890" 100 | s2 = hex_to_charset(s, string.digits) 101 | self.assertTrue(is_valid_int(s2)) 102 | 103 | def test_charset_to_hex(self): 104 | s = ("9859361987058468004536324485715460737416376519236217359320056" 105 | "4555477131708560") 106 | s2 = charset_to_hex(s, string.digits) 107 | self.assertTrue(is_hex(s2)) 108 | 109 | 110 | class ChangeCharsetTests(unittest.TestCase): 111 | def setUp(self): 112 | pass 113 | 114 | def tearDown(self): 115 | pass 116 | 117 | 118 | class ScrubDictTests(unittest.TestCase): 119 | def setUp(self): 120 | pass 121 | 122 | def tearDown(self): 123 | pass 124 | 125 | def test_nested_dict(self): 126 | d = {"a": {"b": {"c": "", "d": [{"e": ""}]}}} 127 | d = scrub_dict(d) 128 | self.assertEqual(len(d), 0) 129 | 130 | 131 | class ToDictTests(unittest.TestCase): 132 | def setUp(self): 133 | pass 134 | 135 | def tearDown(self): 136 | pass 137 | 138 | def test_class_instance_to_dict(self): 139 | class Thing(): 140 | def __init__(self): 141 | self.name = "My Class" 142 | thing = Thing() 143 | d = to_dict(thing) 144 | self.assertTrue(isinstance(d, dict)) 145 | self.assertEqual('name' in d and d['name'], "My Class") 146 | 147 | def test_str_to_dict(self): 148 | s = "a" 149 | try: 150 | d = to_dict(a) 151 | except: 152 | self.assertTrue(True) 153 | else: 154 | self.assertTrue(False) 155 | 156 | def test_int_to_dict(self): 157 | i = 1 158 | try: 159 | d = to_dict(i) 160 | except: 161 | self.assertTrue(True) 162 | else: 163 | self.assertTrue(False) 164 | 165 | def test_dict_to_dict(self): 166 | d = {} 167 | d = to_dict(d) 168 | self.assertTrue(isinstance(d, dict)) 169 | 170 | 171 | class EntropyTests(unittest.TestCase): 172 | def setUp(self): 173 | pass 174 | 175 | def tearDown(self): 176 | pass 177 | 178 | def test_dev_urandom_entropy(self): 179 | bytes16 = dev_urandom_entropy(16) 180 | self.assertEqual(len(bytes16), 16) 181 | 182 | def test_dev_random_entropy(self): 183 | bytes16 = dev_random_entropy(16) 184 | self.assertEqual(len(bytes16), 16) 185 | 186 | def test_dev_random_entropy_fallback_on_nt_operating_system(self): 187 | os.name = 'nt' 188 | bytes16 = dev_random_entropy(16) 189 | self.assertEqual(len(bytes16), 16) 190 | 191 | def test_random_integer(self): 192 | min_value, max_value = 0, sys.maxint 193 | r = secure_randint(min_value, max_value) 194 | self.assertTrue(r > min_value) 195 | self.assertTrue(r < max_value) 196 | 197 | def test_random_integer_larger_than_maxint(self): 198 | min_value, max_value = 0, sys.maxint*2 199 | r = secure_randint(min_value, max_value) 200 | self.assertTrue(r > min_value) 201 | self.assertTrue(r < max_value) 202 | 203 | 204 | def test_main(): 205 | test_support.run_unittest( 206 | ToDictTests, 207 | ScrubDictTests, 208 | IntToCharsetTests, 209 | CharsetToIntTests, 210 | ChangeCharsetTests, 211 | Base16Tests, 212 | EntropyTests 213 | ) 214 | 215 | if __name__ == '__main__': 216 | test_main() 217 | -------------------------------------------------------------------------------- /utilitybelt/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Utilitybelt 4 | ~~~~~ 5 | 6 | :copyright: (c) 2015 by Halfmoon Labs 7 | :license: MIT, see LICENSE for more details. 8 | """ 9 | 10 | __version__ = '0.2.2' 11 | 12 | from .dicts import recursive_dict, scrub_dict, to_dict, recursive_dict_to_dict 13 | from .charsets import int_to_charset, charset_to_int, change_charset 14 | from .charsets import base16_chars, base58_chars, base32_chars, zbase32_chars, \ 15 | base64_chars 16 | from .base16 import hex_to_int, int_to_hex, hex_to_charset, charset_to_hex, \ 17 | hexpad, is_hex, is_int, is_valid_int 18 | from .entropy import dev_urandom_entropy, dev_random_entropy, secure_randint 19 | -------------------------------------------------------------------------------- /utilitybelt/base16.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Utilitybelt 4 | ~~~~~ 5 | 6 | :copyright: (c) 2015 by Halfmoon Labs 7 | :license: MIT, see LICENSE for more details. 8 | """ 9 | 10 | import string 11 | from .charsets import change_charset 12 | 13 | B16_CHARS = string.hexdigits[0:16] 14 | B16_REGEX = '^[0-9a-f]*$' 15 | 16 | 17 | def hex_to_int(s): 18 | try: 19 | return int(s, 16) 20 | except: 21 | raise ValueError("Value must be in hex format") 22 | 23 | 24 | def int_to_hex(i): 25 | try: 26 | return hex(i).rstrip('L').lstrip('0x') 27 | except: 28 | raise ValueError("Value must be in int format") 29 | 30 | 31 | def is_hex(s): 32 | # make sure that s is a string 33 | if not isinstance(s, str): 34 | return False 35 | # if there's a leading hex string indicator, strip it 36 | if s[0:2] == '0x': 37 | s = s[2:] 38 | # try to cast the string as an int 39 | try: 40 | i = hex_to_int(s) 41 | except ValueError: 42 | return False 43 | else: 44 | return True 45 | 46 | 47 | def is_int(i): 48 | return isinstance(i, (int,long)) 49 | 50 | 51 | def is_valid_int(i): 52 | if is_int(i): 53 | return True 54 | elif isinstance(i, str): 55 | try: 56 | int_i = int(i) 57 | except: 58 | return False 59 | else: 60 | return True 61 | return False 62 | 63 | 64 | def hexpad(x): 65 | return ('0' * (len(x) % 2)) + x 66 | 67 | 68 | def charset_to_hex(s, original_charset): 69 | return hexpad(change_charset(s, original_charset, B16_CHARS)) 70 | 71 | 72 | def hex_to_charset(s, destination_charset): 73 | if not is_hex(s): 74 | raise ValueError("Value must be in hex format") 75 | s = s.lower() 76 | return change_charset(s, B16_CHARS, destination_charset) 77 | -------------------------------------------------------------------------------- /utilitybelt/charsets.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Utilitybelt 4 | ~~~~~ 5 | 6 | :copyright: (c) 2015 by Halfmoon Labs 7 | :license: MIT, see LICENSE for more details. 8 | """ 9 | 10 | import re 11 | import string 12 | 13 | 14 | def int_to_charset(val, charset): 15 | """ Turn a non-negative integer into a string. 16 | """ 17 | if not val >= 0: 18 | raise ValueError('"val" must be a non-negative integer.') 19 | if val == 0: 20 | return charset[0] 21 | output = "" 22 | while val > 0: 23 | val, digit = divmod(val, len(charset)) 24 | output += charset[digit] 25 | # reverse the characters in the output and return 26 | return output[::-1] 27 | 28 | 29 | def charset_to_int(s, charset): 30 | """ Turn a string into a non-negative integer. 31 | """ 32 | output = 0 33 | for char in s: 34 | output = output * len(charset) + charset.index(char) 35 | return output 36 | 37 | 38 | def change_charset(s, original_charset, target_charset): 39 | """ Convert a string from one charset to another. 40 | """ 41 | if not isinstance(s, str): 42 | raise ValueError('"s" must be a string.') 43 | 44 | intermediate_integer = charset_to_int(s, original_charset) 45 | output_string = int_to_charset(intermediate_integer, target_charset) 46 | return output_string 47 | 48 | 49 | """ Base16 includes numeric digits and the letters a through f. Here, 50 | we use the lowecase letters. 51 | """ 52 | base16_chars = '0123456789abcdef' 53 | 54 | """ The Base58 character set allows for strings that avoid visual ambiguity 55 | when typed. It consists of all the alphanumeric characters except for 56 | "0", "O", "I", and "l", which look similar in some fonts. 57 | 58 | https://en.bitcoin.it/wiki/Base58Check_encoding 59 | """ 60 | base58_chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" 61 | 62 | """ The Base32 character set allows for accurate transcribing by hand. 63 | It consists of uppercase letters + numerals, excluding "0", "1", + "8", 64 | which could look similar to "O", "I", and "B" and so are omitted. 65 | 66 | http://en.wikipedia.org/wiki/Base32 67 | """ 68 | base32_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" 69 | 70 | """ The z-base-32 character set is similar to the standard Base32 character 71 | set, except it uses lowercase letters + numerals and chooses to exclude 72 | "0", "l", "v", + "2". The set is also permuted so that easier chars 73 | occur more frequently. 74 | 75 | http://philzimmermann.com/docs/human-oriented-base-32-encoding.txt 76 | """ 77 | zbase32_chars = "ybndrfg8ejkmcpqxot1uwisza345h769" 78 | 79 | """ The Base64 character set is a popular encoding for transmitting data 80 | over media that are designed for textual data. It includes all alphanumeric 81 | characters plus two bonus characters, usually "+" and "/". 82 | 83 | http://en.wikipedia.org/wiki/Base64 84 | """ 85 | base64_chars = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 86 | "0123456789+/") 87 | -------------------------------------------------------------------------------- /utilitybelt/dicts.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Utilitybelt 4 | ~~~~~ 5 | 6 | :copyright: (c) 2015 by Halfmoon Labs 7 | :license: MIT, see LICENSE for more details. 8 | """ 9 | 10 | from collections import defaultdict 11 | 12 | """ A recursive dictionary based on the collections lib defaultdict class. 13 | """ 14 | recursive_dict = lambda: defaultdict(recursive_dict) 15 | 16 | 17 | def recursive_dict_to_dict(rdict): 18 | """ Convert a recursive dict to a plain ol' dict. 19 | """ 20 | d = {} 21 | for (k, v) in rdict.items(): 22 | if isinstance(v, defaultdict): 23 | d[k] = recursive_dict_to_dict(v) 24 | else: 25 | d[k] = v 26 | return d 27 | 28 | 29 | def scrub_dict(d): 30 | """ Recursively inspect a dictionary and remove all empty values, including 31 | empty strings, lists, and dictionaries. 32 | """ 33 | if type(d) is dict: 34 | return dict( 35 | (k, scrub_dict(v)) for k, v in d.iteritems() if v and scrub_dict(v) 36 | ) 37 | elif type(d) is list: 38 | return [ 39 | scrub_dict(v) for v in d if v and scrub_dict(v) 40 | ] 41 | else: 42 | return d 43 | 44 | 45 | def _to_json_type(obj, classkey=None): 46 | """ Recursively convert the object instance into a valid JSON type. 47 | """ 48 | if isinstance(obj, dict): 49 | data = {} 50 | for (k, v) in obj.items(): 51 | data[k] = _to_json_type(v, classkey) 52 | return data 53 | elif hasattr(obj, "_ast"): 54 | return _to_json_type(obj._ast()) 55 | elif hasattr(obj, "__iter__"): 56 | return [_to_json_type(v, classkey) for v in obj] 57 | elif hasattr(obj, "__dict__"): 58 | data = dict([ 59 | (key, _to_json_type(value, classkey)) 60 | for key, value in obj.__dict__.iteritems() 61 | if not callable(value) and not key.startswith('_') 62 | ]) 63 | if classkey is not None and hasattr(obj, "__class__"): 64 | data[classkey] = obj.__class__.__name__ 65 | return data 66 | else: 67 | return obj 68 | 69 | 70 | def to_dict(obj): 71 | """ Convert an instance of an object into a dict. 72 | """ 73 | d = _to_json_type(obj) 74 | if isinstance(d, dict): 75 | return scrub_dict(d) 76 | else: 77 | raise ValueError("The value provided must be an object.") 78 | -------------------------------------------------------------------------------- /utilitybelt/entropy.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Utilitybelt 4 | ~~~~~ 5 | 6 | :copyright: (c) 2015 by Halfmoon Labs 7 | :license: MIT, see LICENSE for more details. 8 | """ 9 | 10 | import os 11 | import binascii 12 | import random 13 | 14 | 15 | def dev_urandom_entropy(numbytes): 16 | """ Reads random bytes from the /dev/urandom pool. 17 | 18 | NOTE: /dev/urandom is a non-blocking pseudorandom number generator. 19 | If the entropy pool runs out, previously released entropy will be used. 20 | If blocking is unnacceptable, use this over "dev_urandom_entropy". 21 | """ 22 | return os.urandom(numbytes) 23 | 24 | 25 | def dev_random_entropy(numbytes, fallback_to_urandom=True): 26 | """ Reads random bytes from the /dev/random entropy pool. 27 | 28 | NOTE: /dev/random is a blocking pseudorandom number generator. 29 | If the entropy pool runs out, this function will block until more 30 | environmental noise is gathered. 31 | If entropy re-use is unnacceptable use this over "dev_urandom_entropy". 32 | 33 | If "fallback_to_urandom" is set, this function will fallback to 34 | /dev/urandom on operating systems without /dev/random. 35 | """ 36 | if os.name == 'nt' and fallback_to_urandom: 37 | return dev_urandom_entropy(numbytes) 38 | return open("/dev/random", "rb").read(numbytes) 39 | 40 | 41 | def secure_randint(min_value, max_value, system_random=None): 42 | """ Return a random integer N such that a <= N <= b. 43 | 44 | Uses SystemRandom for generating random numbers. 45 | (which uses os.urandom(), which pulls from /dev/urandom) 46 | """ 47 | if not system_random: 48 | system_random = random.SystemRandom() 49 | return system_random.randint(min_value, max_value) 50 | --------------------------------------------------------------------------------