First version on git
[pwgen2.git] / pwgen2
1 #!/usr/bin/python3
2 #
3 # Password generator from /dev/random
4 # (C) 2012 Nirgal Vourgère - GPL3+ license
5
6 import sys
7 import struct
8 import signal
9
10
11 def entropy_avail():
12         """
13         Returns how many entropy bits the kernel has gathered.
14         This requieres a linux kernel >= 2.3.16.
15         Raises FileNotFoundError if not found
16         """
17         fd = open('/proc/sys/kernel/random/entropy_avail')
18         bits = fd.read()
19         fd.close()
20         bits = int(bits)
21         return bits
22
23 class TimerException(BaseException):
24         pass
25
26 def clock_sighandler(signum, frame):
27         raise TimerException
28
29 def get_password(pw_length, alphabet='ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789'):
30         assert len(alphabet) <= 256, "Alphabet can't have more than 256 choices. Sorry." # We can't go further reading 1 byte
31         random_dev = open('/dev/random', mode='rb', buffering=0)
32         result = ''
33         verbose_length = 0
34         
35         # We want to be notified in 3 seconds if we are not done
36         signal.signal(signal.SIGALRM, clock_sighandler)
37         signal.alarm(3)
38
39         while len(result) < pw_length:
40                 try:
41                         rnd = struct.unpack('B', random_dev.read(1))[0]
42                 except TimerException:
43                         msg = 'Generating passwords. Please wait. Password length: %d bytes, available entropy: %d bits...' % (len(result), entropy_avail())
44                         verbose_length = len(msg)
45                         print('\r' + msg, end='', file=sys.stderr)
46                         signal.alarm(3)
47                         continue
48                 result += alphabet[rnd % len(alphabet)]
49         signal.signal(signal.SIGALRM, signal.SIG_IGN)
50         random_dev.close()
51         if verbose_length:
52                 print(file=sys.stderr)
53         return result
54
55 if __name__ == "__main__":
56         import argparse
57
58         parser = argparse.ArgumentParser(description='Generate random passwords.')
59         parser.add_argument('pw_length', metavar='pw_length', type=int, nargs='?',
60                 default=16, help='password length')
61         parser.add_argument('num_pw', metavar='num_pw', type=int, nargs='?',
62                 default=1, help='how many passwords to show')
63         parser.add_argument('--show-entropy', action='store_true', default=False,
64                 help='Show entropy count')
65         args = parser.parse_args()
66
67         if (args.show_entropy):
68                 try:
69                         print('Available entropy: %d bytes' % (entropy_avail()//8), file=sys.stderr)
70                 except FileNotFoundError:
71                         print('Your system does not support entropy status repporting.', file=sys.stderr)
72         for i in range(args.num_pw):
73                 print(get_password(args.pw_length))