2021-01-06 04:41:22 +00:00
|
|
|
'''
|
2022-01-10 01:05:03 +00:00
|
|
|
This module provides functions for generating random strings. All functions use
|
|
|
|
cryptographically strong randomness if the operating system supports it, and
|
|
|
|
non-cs randomness if it does not.
|
|
|
|
|
|
|
|
If os.urandom(1) gives you a byte, your system has cs randomness.
|
2021-09-11 18:15:31 +00:00
|
|
|
'''
|
|
|
|
import argparse
|
|
|
|
import math
|
|
|
|
import os
|
|
|
|
import random
|
|
|
|
import string
|
|
|
|
import sys
|
2018-12-18 06:10:00 +00:00
|
|
|
|
2021-09-11 18:15:31 +00:00
|
|
|
from voussoirkit import betterhelp
|
2022-02-13 03:43:23 +00:00
|
|
|
from voussoirkit import gentools
|
2021-09-11 18:15:31 +00:00
|
|
|
from voussoirkit import pipeable
|
|
|
|
|
2022-01-10 01:05:03 +00:00
|
|
|
try:
|
|
|
|
os.urandom(1)
|
|
|
|
RNG = random.SystemRandom()
|
|
|
|
except NotImplementedError:
|
|
|
|
RNG = random
|
|
|
|
|
2021-09-11 18:15:31 +00:00
|
|
|
def make_password(
|
|
|
|
length,
|
2022-01-10 01:05:03 +00:00
|
|
|
*,
|
|
|
|
binary=False,
|
2021-09-11 18:15:31 +00:00
|
|
|
digits=False,
|
|
|
|
hex=False,
|
2022-01-10 01:05:03 +00:00
|
|
|
letters=False,
|
2021-09-11 18:15:31 +00:00
|
|
|
punctuation=False,
|
|
|
|
):
|
|
|
|
alphabet = set()
|
|
|
|
if letters:
|
|
|
|
alphabet.update(string.ascii_letters)
|
|
|
|
if digits:
|
|
|
|
alphabet.update(string.digits)
|
|
|
|
if hex:
|
|
|
|
alphabet.update('0123456789abcdef')
|
|
|
|
if binary:
|
|
|
|
alphabet.update('01')
|
|
|
|
if punctuation:
|
|
|
|
alphabet.update(string.punctuation)
|
|
|
|
|
|
|
|
if not alphabet:
|
|
|
|
raise ValueError('No alphabet options chosen.')
|
|
|
|
|
2022-01-10 01:05:03 +00:00
|
|
|
return ''.join(RNG.choices(tuple(alphabet), k=length))
|
2021-09-11 18:15:31 +00:00
|
|
|
|
|
|
|
def make_sentence(length, separator=' '):
|
2018-12-18 06:10:00 +00:00
|
|
|
'''
|
|
|
|
Returns a string containing `length` words, which come from
|
|
|
|
dictionary.common.
|
|
|
|
'''
|
|
|
|
import dictionary.common as common
|
2022-01-10 01:05:03 +00:00
|
|
|
words = RNG.choices(common.words, k=length)
|
2021-09-11 18:15:31 +00:00
|
|
|
words = [w.replace(' ', separator) for w in words]
|
|
|
|
result = separator.join(words)
|
2018-12-18 06:10:00 +00:00
|
|
|
return result
|
|
|
|
|
2022-01-10 01:05:03 +00:00
|
|
|
def random_digits(length):
|
|
|
|
'''
|
|
|
|
Shortcut function for when you don't want to type the make_password call.
|
|
|
|
'''
|
|
|
|
return ''.join(RNG.choices(string.digits, k=length))
|
2018-12-18 06:10:00 +00:00
|
|
|
|
2022-01-10 01:05:03 +00:00
|
|
|
def random_hex(length):
|
|
|
|
'''
|
|
|
|
Shortcut function for when you don't want to type the make_password call.
|
|
|
|
'''
|
2021-01-06 04:41:22 +00:00
|
|
|
randbytes = os.urandom(math.ceil(length / 2))
|
|
|
|
token = ''.join('{:02x}'.format(x) for x in randbytes)
|
|
|
|
token = token[:length]
|
|
|
|
return token
|
2018-12-18 06:10:00 +00:00
|
|
|
|
2021-09-11 18:15:31 +00:00
|
|
|
def passwordy_argparse(args):
|
|
|
|
if args.sentence:
|
|
|
|
password = make_sentence(args.length, args.separator)
|
2020-01-22 01:46:25 +00:00
|
|
|
else:
|
2021-09-11 18:15:31 +00:00
|
|
|
if not any([args.letters, args.digits, args.hex, args.binary, args.punctuation]):
|
|
|
|
letters = True
|
2022-03-10 19:18:21 +00:00
|
|
|
digits = True
|
2021-09-11 18:15:31 +00:00
|
|
|
else:
|
|
|
|
letters = args.letters
|
2022-03-10 19:18:21 +00:00
|
|
|
digits = args.digits
|
2021-09-11 18:15:31 +00:00
|
|
|
password = make_password(
|
2022-01-10 01:05:03 +00:00
|
|
|
args.length,
|
|
|
|
binary=args.binary,
|
2022-03-10 19:18:21 +00:00
|
|
|
digits=digits,
|
2021-09-11 18:15:31 +00:00
|
|
|
hex=args.hex,
|
2022-01-10 01:05:03 +00:00
|
|
|
letters=letters,
|
2021-09-11 18:15:31 +00:00
|
|
|
punctuation=args.punctuation,
|
|
|
|
)
|
|
|
|
if args.lower:
|
|
|
|
password = password.lower()
|
|
|
|
elif args.upper:
|
|
|
|
password = password.upper()
|
2022-01-20 04:23:46 +00:00
|
|
|
|
2022-02-13 03:43:23 +00:00
|
|
|
if args.groups_of is not None:
|
|
|
|
chunks = gentools.chunk_generator(password, args.groups_of)
|
|
|
|
chunks = (''.join(chunk) for chunk in chunks)
|
|
|
|
password = args.separator.join(chunks)
|
|
|
|
|
2022-01-20 04:23:46 +00:00
|
|
|
prefix = args.prefix or ''
|
|
|
|
suffix = args.suffix or ''
|
|
|
|
password = f'{prefix}{password}{suffix}'
|
|
|
|
|
2021-09-11 18:15:31 +00:00
|
|
|
pipeable.stdout(password)
|
|
|
|
return 0
|
2020-01-22 01:46:25 +00:00
|
|
|
|
2021-09-11 18:15:31 +00:00
|
|
|
def main(argv):
|
2022-02-13 03:43:23 +00:00
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
description='''
|
|
|
|
Generate random passwords using cryptographically strong randomness.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.examples = [
|
|
|
|
{'args': '32 --letters --digits --punctuation', 'run': True},
|
|
|
|
{'args': '48 --hex --upper', 'run': True},
|
|
|
|
{'args': '8 --sentence --separator +', 'run': True},
|
|
|
|
{'args': '16 --digits --groups-of 4 --separator -', 'run': True},
|
|
|
|
{'args': '48 --prefix example.com_ --lower', 'run': True},
|
|
|
|
]
|
|
|
|
parser.add_argument(
|
|
|
|
'length',
|
|
|
|
type=int,
|
|
|
|
help='''
|
|
|
|
Integer number of characters in normal mode.
|
|
|
|
Integer number of words in sentence mode.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--sentence',
|
|
|
|
action='store_true',
|
|
|
|
help='''
|
|
|
|
If this argument is passed, the password is made of length random words and
|
|
|
|
the other alphabet options are ignored.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--groups_of', '--groups-of',
|
|
|
|
type=int,
|
|
|
|
help='''
|
|
|
|
Split the password up into chunks of this many characters, and join them
|
|
|
|
back together with the --separator.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--separator',
|
|
|
|
type=str,
|
|
|
|
default=' ',
|
|
|
|
help='''
|
|
|
|
In sentence mode, the words will be joined with this string.
|
|
|
|
In normal mode, the --groups-of chunks will be joined with this string.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--letters',
|
|
|
|
action='store_true',
|
|
|
|
help='''
|
|
|
|
Include ASCII letters in the password.
|
|
|
|
If none of the other following options are chosen, letters is the default.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--digits',
|
|
|
|
action='store_true',
|
|
|
|
help='''
|
|
|
|
Include digits 0-9 in the password.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--hex',
|
|
|
|
action='store_true',
|
|
|
|
help='''
|
|
|
|
Include 0-9, a-f in the password.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--binary',
|
|
|
|
action='store_true',
|
|
|
|
help='''
|
|
|
|
Include 0, 1 in the password.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--punctuation',
|
|
|
|
action='store_true',
|
|
|
|
help='''
|
|
|
|
Include punctuation symbols in the password.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--prefix',
|
|
|
|
type=str,
|
|
|
|
default=None,
|
|
|
|
help='''
|
|
|
|
Add a static prefix to the password.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--suffix',
|
|
|
|
type=str,
|
|
|
|
default=None,
|
|
|
|
help='''
|
|
|
|
Add a static suffix to the password.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--lower',
|
|
|
|
action='store_true',
|
|
|
|
help='''
|
|
|
|
Convert the entire password to lowercase.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--upper',
|
|
|
|
action='store_true',
|
|
|
|
help='''
|
|
|
|
Convert the entire password to uppercase.
|
|
|
|
''',
|
|
|
|
)
|
2021-09-11 18:15:31 +00:00
|
|
|
parser.set_defaults(func=passwordy_argparse)
|
|
|
|
|
2022-02-13 03:43:23 +00:00
|
|
|
return betterhelp.go(parser, argv)
|
2020-01-22 01:46:25 +00:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
raise SystemExit(main(sys.argv[1:]))
|