{"id":1277,"date":"2021-06-27T01:53:56","date_gmt":"2021-06-27T01:53:56","guid":{"rendered":"https:\/\/www.ishygddt.xyz\/~blog\/?p=1277"},"modified":"2022-03-08T18:43:46","modified_gmt":"2022-03-08T18:43:46","slug":"python-xofs-as-random","status":"publish","type":"post","link":"http:\/\/www.ishygddt.xyz\/~blog\/2021\/06\/python-xofs-as-random","title":{"rendered":"Python: Using XOFs for general-purpose Random"},"content":{"rendered":"<p>As always, one's own stack overflow answers make the best blog posts.<\/p>\n<p><a href=\"https:\/\/crypto.stackexchange.com\/a\/90245\/8287\">In this case<\/a>, we craft a version of <code class=\"language-python\" data-line=\"\">random.Random<\/code> with a few modifications:<\/p>\n<ul>\n<li>Pulls its data from an arbitrary stream (in our case, a <a href=\"https:\/\/crypto.stackexchange.com\/a\/35007\/8287\">DRBG<\/a> such as a hash function or deterministic CSPRNG)<\/li>\n<li>Wastes noticeably fewer bits when generating random integers<\/li>\n<li>Has fixed code for <code class=\"language-python\" data-line=\"\">.shuffle<\/code>, on the offchance CPython ever changes theirs, and to make it work with other implementations<\/li>\n<\/ul>\n<pre><code class=\"language-python\" data-line=\"\">from random import Random\nfrom resource import getpagesize as _getpagesize\nfrom functools import reduce as _reduce\nfrom itertools import islice as _islice, repeat as _repeat\n\n\nclass StreamBasedRandom(Random):\n\tdef __init__(self, stream, blocksize=_getpagesize()):\n\t\tself._randbitgen = _ibytestobits(map(stream.read, _repeat(blocksize)))\n\tdef getrandbits(self, k):\n\t\treturn _concatbits(_islice(self._randbitgen, k))\n\t# Fix the following functions to prevent implementation-dependency\n\tdef randbytes(self, n):\n\t\treturn self.getrandbits(n * 8).to_bytes(n, &#039;big&#039;)\n\tdef _randbelow(self, n):\n\t\t&quot;&quot;&quot;Replacement for CPython&#039;s Random._randbelow that wastes extremely few bits&quot;&quot;&quot;\n\t\ta = 0\n\t\tb = 1\n\t\twhile True:\n\t\t\ta = (a * 2) | getrandbits(1)\n\t\t\tb = b * 2\n\t\t\tif b &gt;= n:\n\t\t\t\tif a &lt; n:\n\t\t\t\t\treturn a\n\t\t\t\ta = a - n\n\t\t\t\tb = b - n\n\tdef shuffle(self, x):\n\t\t&quot;&quot;&quot;Modern Fisher-Yates shuffle&quot;&quot;&quot;\n\t\trandbelow = self._randbelow\n\t\tfor i in reversed(range(1, len(x))):\n\t\t\tj = randbelow(i + 1)\n\t\t\tx[i], x[j] = x[j], x[i]\n\n\ndef _ibytestobits(ibytes):\n\t&quot;&quot;&quot;Turns an iterator of bytes into an iterator of its component bits, big-endian&quot;&quot;&quot;\n\tyield from ((i &gt;&gt; k) &amp; 0b1 for b in ibytes for i in b for k in reversed(range(8)))\n\ndef _concatbits(bits):\n\t&quot;&quot;&quot;Takes a finite iterator of bits and returns their big-endian concatenation as an integer&quot;&quot;&quot;\n\treturn _reduce((lambda acc, cur: ((acc &lt;&lt; 1) | cur)), bits, 0)\n<\/code><\/pre>\n<p>the example for which this was created:<\/p>\n<pre><code class=\"language-python\" data-line=\"\">from Cryptodome.Hash import SHAKE256\n\ndef deterministic_shuffle(seq, key, alg=SHAKE256):\n\t# h\/t https:\/\/crypto.stackexchange.com\/a\/90245\/8287\n\t&quot;&quot;&quot;Applies a pseudorandom permutation from key to seq&quot;&quot;&quot;\n\trandom = StreamBasedRandom(stream=alg.new(key), blocksize=136)\n\trandom.shuffle(seq)<\/code><\/pre>\n<p>and a more absurd\/reductionist example showing that a CSPRNG is a CSPRNG, no matter whether it identifies as a hash function or a stream cipher:<\/p>\n<pre><code class=\"language-python\" data-line=\"\">import os\nfrom Cryptodome.Cipher import AES\nfrom types import SimpleNamespace\n\ndef aes_random_factory(seed, nonce=b&#039;&#039;):\n\t# CTR is used to allow future implementers to trivially seek without breaking compatibility\n\tcipher = AES.new(seed, AES.MODE_CTR, nonce=nonce)\n\tdef randbytes(n):\n\t\treturn cipher.encrypt(b&#039;\\x00&#039; * n)\n\tstream = SimpleNamespace(read=randbytes)\n\treturn StreamBasedRandom(stream=stream, blocksize=cipher.block_size)\n\nseed = os.urandom(32)\nrandom = aes_random_factory(seed)\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>As always, one's own stack overflow answers make the best blog posts. In this case, we craft a version of random.Random with a few modifications: Pulls its data from an arbitrary stream (in our case, a DRBG such as a hash function or deterministic CSPRNG) Wastes noticeably fewer bits when generating random integers Has fixed &hellip;<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[97],"tags":[102,44,90],"class_list":["post-1277","post","type-post","status-publish","format-standard","hentry","category-original-content","tag-cryptography","tag-python","tag-rng"],"_links":{"self":[{"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/posts\/1277","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/comments?post=1277"}],"version-history":[{"count":55,"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/posts\/1277\/revisions"}],"predecessor-version":[{"id":1957,"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/posts\/1277\/revisions\/1957"}],"wp:attachment":[{"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/media?parent=1277"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/categories?post=1277"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/tags?post=1277"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}