# Copyright (c) 2000 Autonomous Zone Industries # Copyright (c) 2002-2006 Bryce "Zooko" Wilcox-O'Hearn # mailto:zooko@zooko.com # This file is licensed under the # GNU Lesser General Public License v2.1. # See the file COPYING or visit http://www.gnu.org/ for details. """ Cryptographically strong pseudo-random number generator based on SHA256. """ class SHA256Expander: """ Provide a cryptographically strong pseudo-random number generator based on SHA256. Hopefully this means that no attacker will be able to predict any bit of output that he hasn't seen, given that he doesn't know anything about the seed and given that he can see as many bits of output as he desires except for the bit that he is trying to predict. Further it is hoped that an attacker will not even be able to determine whether a given stream of random bytes was generated by this PRNG or by flipping a coin repeatedly. The safety of this technique has not been verified by a Real Cryptographer. ... but it is similar to the PRNG in FIPS-186... The seed and counter are encoded in DJB's netstring format so that I don't have to think about the possibility of ambiguity. """ def __init__(self, seed=None): if seed is not None: self.seed(seed) def seed(self, seed): import hashlib self.starth = hashlib.sha256('24:pyutil hash expansion v2,10:algorithm:,6:SHA256,6:value:,') seedlen = len(seed) seedlenstr = str(seedlen) self.starth.update(seedlenstr) self.starth.update(':') self.starth.update(seed) self.starth.update(',') self.avail = "" self.counter = 0 def get(self, bytes): bytesleft = bytes res = [] while bytesleft > 0: if len(self.avail) == 0: h = self.starth.copy() counterstr = str(self.counter) counterstrlen = len(counterstr) counterstrlenstr = str(counterstrlen) h.update(counterstrlenstr) h.update(':') h.update(counterstr) h.update(',') self.avail = h.digest() self.counter += 1 numb = min(len(self.avail), bytesleft) (chunk, self.avail,) = (self.avail[:numb], self.avail[numb:],) res.append(chunk) bytesleft = bytesleft - numb resstr = ''.join(res) assert len(resstr) == bytes return resstr def sha256expand(inpstr, expbytes): return SHA256Expander(inpstr).get(expbytes)