diff options
author | Dennis Eriksen <d@ennis.no> | 2023-11-09 21:56:52 +0100 |
---|---|---|
committer | Dennis Eriksen <d@ennis.no> | 2023-11-09 21:56:52 +0100 |
commit | 5b220f2dfd033dde08b602929ad665ce3dd2d183 (patch) | |
tree | 1c521f71b5356bda0c2121d54386841db8c21bf3 /makepass.py | |
parent | Updated build-instructions for go-version (diff) | |
download | makepass-5b220f2dfd033dde08b602929ad665ce3dd2d183.tar.gz |
Adding a python-verion
Diffstat (limited to '')
-rwxr-xr-x | makepass.py | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/makepass.py b/makepass.py new file mode 100755 index 0000000..233ae7d --- /dev/null +++ b/makepass.py @@ -0,0 +1,318 @@ +#!/usr/bin/env python3 +# +# Author : Dennis Eriksen <d@ennis.no> +# File : makepass.py +# Created : 2023-09-14 +# License : BSD-3-Clause +# +# Copyright (c) 2018-2023 Dennis Eriksen <d@ennis.no> + +import argparse +import os +import random +import sys + +# Global variables + +MAX = 255 +RANGE_MAX = 42 +RANGE_MIN = 8 +PASS_WORDS = 8 + +LOWER = "abcdefghijklmnopqrstuvwxyz" +UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +DIGIT = "0123456789" +OTHER = "!$%&#/()=?+-_,.;:<>[]{}|@*" + +ALPHA = LOWER + UPPER +ALNUM = ALPHA + DIGIT +EVERY = ALNUM + OTHER + +NORMAL = ALNUM + "_-" +SPECIAL = EVERY + +DEFAULT_LENGTH = 0 +DEFAULT_NUMBER = 10 +DEFAULT_WORDLIST = "/usr/share/dict/words" + + +class CommonPrintData: + def __init__(self, length, printbool, printlen, col_width, col_num): + self.length = length + self.printbool = printbool + self.printlen = printlen + self.col_num = col_num + self.col_width = col_width + + +# +# Main function +# +def main(): + normal = os.getenv("MAKEPASS_NORMAL") or NORMAL + special = os.getenv("MAKEPASS_SPECIAL") or SPECIAL + wordlist_file = os.getenv("MAKEPASS_WORDLIST") or DEFAULT_WORDLIST + + # Initialize parser + parser = argparse.ArgumentParser( + description="Process some strings.", add_help=False + ) + + # Add arguments + parser.add_argument("-h", "--help", action="store_true") + parser.add_argument("-l", "--length", type=int, help="Length of strings") + parser.add_argument("-n", "--number", type=int, help="Number of strings") + parser.add_argument("-p", "--printlen", action="store_true") + parser.add_argument("arg_length", type=int, nargs="?") + + # Parse arguments + args = parser.parse_args() + + if args.help: + usage() + + # Some error-checking + # length + try: + # env or default + length = int(os.getenv("MAKEPASS_LENGTH") or DEFAULT_LENGTH) + + # -l flag + if args.length is not None: + if args.length < 0 or MAX < args.length: + raise ValueError("-l") + length = args.length + + # solo argument + if args.arg_length is not None: + length = args.arg_length + + # check result + if length < 0 or MAX < length: + raise ValueError("length") + except ValueError as e: + print(f"{e} must be a number between 0 and {MAX}") + sys.exit(1) + + # number + try: + # env or default + number = int(os.getenv("MAKEPASS_NUMBER") or DEFAULT_NUMBER) + + # -n flag + if args.number is not None: + if args.number < 1 or MAX < args.number: + raise ValueError("-n") + number = args.number + + # check result + if number < 1 or MAX < number: + raise ValueError("number") + except ValueError as e: + print(f"{e} must be a number between 1 and {MAX}") + sys.exit(1) + + # Set some other stuff + + printbool = args.printlen + if printbool: + printlen = 3 if length < 100 else 4 + else: + printlen = 0 + + # get terminal width + try: + xwidth = os.get_terminal_size().columns + except Exception as _: + xwidth = 1 + + # get width of columns to print + if length == 0: + col_width = RANGE_MAX + 2 + else: + col_width = length + 2 + + # number of columns to print + col_num = int(xwidth / (col_width + printlen)) + if col_num == 0: + col_num = 1 + + # config + config = CommonPrintData( + length=length, + printbool=printbool, + printlen=printlen, + col_width=col_width, + col_num=col_num, + ) + + # + # print passwords + # + + # Generate and print normal and special passwords + print_columns("Normal passwords", number, normal, config) + + print() + + print_columns( + "Passwords with special characters", int(number / 3 * 2 + 1), special, config + ) + + # Generate and print passphrases if wordlist exists + if os.path.isfile(wordlist_file): + print() + + print("Generating passphrases: ") + passphrase(int(number / 2), wordlist_file) + + +# +# Print passwords in neat columns +# +def print_columns(title: str, num: int, chars: str, c: CommonPrintData): + # Generate random strings + strings = [randstring(c.length, chars) for _ in range(num)] + + print(f"{title}:") + + for i in range(num): + # Print the length of the string if printlen is set + if c.printbool: + print(f"{len(strings[i]):0{c.printlen-1}}", end=" ") + + # Print the string, left justified based on column width + print(f"{strings[i]:<{c.col_width}}", end="") + + # If we have printed enough strings for a single line or the + # rest of strings are fewer than column number, we break lines. + i += 1 + if i % c.col_num == 0 or (i == num and i % c.col_num > 0): + print() + + +# +# Generate random strings that can work as passwords +# +def randstring(length, chars): + """ + Generate a random password of the specified length. Use special + characters if special_chars option is True. + """ + if length == 0: + length = random.randint(RANGE_MIN, RANGE_MAX) + + password = [] + password.append(random.choice(ALPHA)) + for i in range(length - 2): + password.append(random.choice(chars)) + password.append(random.choice(ALNUM)) + + # Randomly reorder the characters + return "".join(password) + + +# +# Passphrases! +# +def passphrase(number, file_name): + """ + Generate a number of random passphrases from the wordlist. + """ + try: + with open(file_name, "r") as f: + lines = f.read().splitlines() + except IOError: + print(f"Cannot open file: {file_name}") + sys.exit(1) + + for i in range(number): + word_list = random.choices(lines, k=5) + passphrase = "-".join(word_list) + print(passphrase) + + +def usage(): + text = """NAME + makepass - create several random passwords + +SYNOPSIS + makepass [OPTIONS] [NUM] + + If a NUM is provided, passwords will be NUM characters long. + + By default `makepass` will output passwords from the three following classes: + + - Normal passwords - random strings with letters (both lower and upper + case), numbers, and dashes and underscores. + + - Passwords with special characters - random strings generated from lower + and upper case letters, numbers, and the following characters: + !#$%&/()=?+-_,.;:<>[]{}|@* + + - Passphrases - if we find a dictionary, a series of eight random words + from the dictionary, separated by dashes. The number of words can not be + changed, but you do not have to use all of them. Use as mane as you want. + + The first character will always be alphabetic, and the last will always be + alphanumeric. + +DESCRIPTION + makepass has the following options: + + -h + output this help-text + -l + length of passwords. See MAKEPASS_LENGTH below + -n + number of passwords. See MAKEPASS_NUMBER below + -p + print length of number + +ENVIRONMENT + makepass examines the following environmental variables. + + MAKEPASS_LENGTH + Specifies the length of passwords. Valid values are 0-255. If 0, a + random value between 8 and 42 will be used for each password. -l + overrides this environmental variable, and the argument NUM overrides + that again. So `MAKEPASS_LENGTH=10 makepass -l 12 14` will give + passwords that are 14 characters long, even though both -l and + MAKEPASS_LENGTH also specifies a length. + + MAKEPASS_NUMBER + The number of passwords to generate. This formula is used to determine + how many passwords from each group should be generated: + - (n) normal passwords + - (n / 3 * 2 + 1) special passwords + - (n / 2) passphrases + Where n is 10 by default. Valid values for n are 1-255. Floating-poing + math is not used, so results may vary. + + MAKEPASS_PRINTLEN + If 1, print length of all passwords. If 0, don\'t. + + MAKEPASS_NORMAL + String of characters from which to generate "normal" passwords. + Defaults to: + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_ + + MAKEPASS_SPECIAL + String of characters from which to generate passwords with special + characters. Defaults to the same characters as in MAKEPASS_NORMAL, plus + these: + !#$%&/()=?+-_,.;:<>[]{}|@* + + MAKEPASS_WORDLIST + Specifies the dictionary we find words for passphrases in. If this is + unset or empty, we try "/usr/share/dict/words". If that file does not + exist, no passphrases will be provided. + +AUTHOR + Dennis Eriksen <https://dnns.no>""" + print(text) + sys.exit(0) + + +if __name__ == "__main__": + main() |