It looks likes what I intended to report as a wish for a --slow option
got some attention recently. See bug #726578.

I'm attaching the little script I wrote in order to understand how slow
a switch to /dev/random would make pwgen. It has a trigger displaying
available entropy when things are too long. It might help other.
Try "pwgen2 16 16" a few times in a row.
#!/usr/bin/python3
#
# Password generator from /dev/random
# (C) 2012 Nirgal Vourgère - GPL3+ license

import sys
import struct
import signal


def entropy_avail():
        """
        Returns how many entropy bits the kernel has gathered.
        This requieres a linux kernel >= 2.3.16.
        Raises FileNotFoundError if not found
        """
        fd = open('/proc/sys/kernel/random/entropy_avail')
        bits = fd.read()
        fd.close()
        bits = int(bits)
        return bits

def dummy_sighandler(signum, frame):
        """
        Dummy signal handler that does nothing.
        But InterruptedError is still raised.
        """
        pass

def get_password(pw_length, 
alphabet='ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789'):
        assert len(alphabet) <= 256, "Alphabet can't have more than 256 
choices. Sorry." # We can't go further reading 1 byte
        random_dev = open('/dev/random', mode='rb', buffering=0)
        result = ''
        verbose_length = 0
        
        # We want to be notified in 3 seconds if we are not done
        signal.signal(signal.SIGALRM, dummy_sighandler)
        signal.alarm(3)

        while len(result) < pw_length:
                try:
                        rnd = struct.unpack('B', random_dev.read(1))[0]
                except InterruptedError:
                        msg = 'Generating passwords. Please wait. Password 
length: %d bytes, available entropy: %d bits...' % (len(result), 
entropy_avail())
                        verbose_length = len(msg)
                        print('\r' + msg, end='', file=sys.stderr)
                        signal.alarm(3)
                        continue
                result += alphabet[rnd % len(alphabet)]
        signal.signal(signal.SIGALRM, signal.SIG_IGN)
        random_dev.close()
        if verbose_length:
                print(file=sys.stderr)
        return result

if __name__ == "__main__":
        import argparse

        parser = argparse.ArgumentParser(description='Generate random 
passwords.')
        parser.add_argument('pw_length', metavar='pw_length', type=int, 
nargs='?',
                default=16, help='password length')
        parser.add_argument('num_pw', metavar='num_pw', type=int, nargs='?',
                default=1, help='how many passwords to show')
        parser.add_argument('--show-entropy', action='store_true', 
default=False,
                help='Show entropy count')
        args = parser.parse_args()

        if (args.show_entropy):
                try:
                        print('Available entropy: %d bytes' % 
(entropy_avail()//8), file=sys.stderr)
                except FileNotFoundError:
                        print('Your system does not support entropy status 
repporting.', file=sys.stderr)
        for i in range(args.num_pw):
                print(get_password(args.pw_length))

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to