""" crypto.entropy.pagingEntropy

    Uses variations in disk access time to generator entropy.  A long string is
    created that is bigger than available memory. Virtual memory access' create
    random variations in retrieval time.

    Just an experiment, not recommended for use at this time.

    Copyright (c) 2002 by Paul A. Lambert
    Read LICENSE.txt for license information.
"""
import struct

class PagingEntropyCollector:
    """ collect entropy from memory paging """
    def __init__(self, memSize=500000000L):            #? how should this be picked?
        """ Initialize paging entropy collector,
            memSize must be larger than allocated memory """
        self.size     = memSize
        self.memBlock = self.size*chr(0) # long string of length self.size
        self.index    = 0
        import random
        self.rand     = random.Random(1555551)

    def randomBytes(self, numberOfBytes, secondsPerBit=.05):
        byteString = ''
        for b in range(numberOfBytes):
            aByte = 0
            for bit in range(8):
                aByte = aByte << 1
                aByte = aByte ^ self.collectBit(secondsPerBit)
            byteString += chr(aByte)
        return byteString

    def collectBit(self, secondsPerBit=1.0):
        """ Collect an entropy bit by jumping around a long string and
            collecting the variation in time and number of samples per
            time interval """
        t1 = time()
        count = 0
        while (time()-t1) < secondsPerBit:           # seconds per sample
            # use random to sample various virtual memory locations
            sample = self.memBlock[int(self.rand.random()*self.size)]
            count += 1
        randomBit = intToParity(count)^floatToParity(time()-t1)
        return randomBit

def intToParity(integer):
    s = struct.pack('i',integer)
    parity = 0
    for character in s:
        byte = ord(character)
        parity = parity^(0x01&(byte^(byte>>1)^(byte>>2)^(byte>>3)^(byte>>4)^(byte>>5)^(byte>>6)^(byte>>7)))
    return parity

def floatToParity(float):
    s = struct.pack('d',float)
    parity = 0
    for character in s:
        byte = ord(character)
        parity = parity^(0x01&(byte^(byte>>1)^(byte>>2)^(byte>>3)^(byte>>4)^(byte>>5)^(byte>>6)^(byte>>7)))
    return parity

if __name__ == "__main__":
    from binascii import b2a_hex
    e = PagingEntropyCollector()
    for i in range(20):
        e.rand.seed(1)        # make each sample set the same to allow examination of statistics
        print b2a_hex( e.randomBytes(16) )






