First version on git master
authorJean-Michel Vourgère <nirgal@debian.org>
Mon, 27 Nov 2017 09:21:37 +0000 (10:21 +0100)
committerJean-Michel Vourgère <nirgal@debian.org>
Mon, 27 Nov 2017 09:21:37 +0000 (10:21 +0100)
pwgen2 [new file with mode: 0755]

diff --git a/pwgen2 b/pwgen2
new file mode 100755 (executable)
index 0000000..7725633
--- /dev/null
+++ b/pwgen2
@@ -0,0 +1,73 @@
+#!/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
+
+class TimerException(BaseException):
+       pass
+
+def clock_sighandler(signum, frame):
+       raise TimerException
+
+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, clock_sighandler)
+       signal.alarm(3)
+
+       while len(result) < pw_length:
+               try:
+                       rnd = struct.unpack('B', random_dev.read(1))[0]
+               except TimerException:
+                       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))