From 4ea3c077bd4111960af1b93195cb25bfb1d6edce Mon Sep 17 00:00:00 2001 From: Simeon Simeonov Date: Thu, 31 Mar 2022 07:21:33 +0200 Subject: Restructure the project in order to replace distutils with setuptools --- otp2289/__main__.py | 385 ---------------------------------------------------- 1 file changed, 385 deletions(-) delete mode 100644 otp2289/__main__.py (limited to 'otp2289/__main__.py') diff --git a/otp2289/__main__.py b/otp2289/__main__.py deleted file mode 100644 index 78e60e8..0000000 --- a/otp2289/__main__.py +++ /dev/null @@ -1,385 +0,0 @@ -# -*- coding: utf-8 -*- -# SPDX-License-Identifier: BSD-2-Clause-FreeBSD -# -# Copyright (c) 2020-2022 Simeon Simeonov -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR -# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" -CLI entry point for the otp2289 package - -Examples: -python -m otp2289 --initiate-new-sequence -s TesT - -python -m otp2289 --generate-otp-response -c "otp-md5 499 TesT " -f token -python -m otp2289 --generate-otp-response -s TesT -i 499 -f token -""" -import argparse -import errno -import getpass -import io -import os -import secrets -import string -import sys - -import otp2289 - - -def eprint(*arg, **kwargs): - """stdderr print wrapper""" - print(*arg, file=sys.stderr, flush=True, **kwargs) - - -def generate_otp_response(args: argparse.Namespace) -> str: - """ - Generates a response based on the parameters sent from the parser - - :param args: The arguments assigned from argparse - :type args: argparse.Namespace - - :raises otp2289.OTPChallengeException: If the challenge is invalid - - :raises otp2289.OTPGeneratorException: If generator parameters are wrong - - :return: The response string - :rtype: str - """ - generator = otp2289.generator.OTPGenerator( - args.password.encode(), - args.seed, - args.hash_algo, - ) - if args.challenge_string: - if args.output_format == 'token': - return generator.generate_otp_words_from_challenge( - args.challenge_string - ) - return generator.generate_otp_hexdigest_from_challenge( - args.challenge_string - ) - # regular parameters - header = '' - if not args.quiet: - header = ( - f'Seed: {args.seed}, Step: {args.step}, ' - f'Hash: {args.hash_algo}{os.linesep}' - ) - if args.output_format == 'token': - return header + generator.generate_otp_words(args.step) - return header + generator.generate_otp_hexdigest(args.step) - - -def generate_otp_range(args: argparse.Namespace) -> str: - """ - Generates range of responses based on the parameters sent from the parser - - :param args: The arguments assigned from argparse - :type args: argparse.Namespace - - :raises otp2289.OTPChallengeException: If the challenge is invalid - - :raises otp2289.OTPGeneratorException: If generator parameters are wrong - - :return: The responses string - :rtype: str - """ - generator = otp2289.generator.OTPGenerator( - args.password.encode(), - args.seed, - args.hash_algo, - ) - if args.output_format == 'token': - method = generator.generate_otp_words - else: - method = generator.generate_otp_hexdigest - # handle most cases explicitly - if args.range == 1: - return f'{args.step}: ' + method(args.step) - if args.range > args.step + 1: - args.range = args.step + 1 - # any need for quiet? - header = '' - if not args.quiet: - header = ( - f'Seed: {args.seed}, Step: {args.step}, ' - f'Hash: {args.hash_algo}, Range: {args.range}{os.linesep}' - ) - return header + os.linesep.join( - [ - f'{step}: ' + method(step) - for step in range( - args.step, - args.step - args.range, - -1, - ) - ] - ) - - -def get_password(args: argparse.Namespace) -> str: - """ - Extract the provided password using the defined argparse arguments - - :param args: The arguments assigned from argparse - :type args: argparse.Namespace - - :raises KeyboardInterrupt: If the password prompt is interrupted - - :return: The extrated password string - :rtype: str - """ - if args.force_password_prompt: - while True: - password = getpass.getpass() - if not args.initiate_new_sequence or password == getpass.getpass( - 'Repeat password: ' - ): - break - eprint('The passwords do not match') - return password - - if not args.password: - password = os.environ.get('OTP2289_PASSWORD') - if password is not None: - return password - while True: - password = getpass.getpass() - if not args.initiate_new_sequence or password == getpass.getpass( - 'Repeat password: ' - ): - break - eprint('The passwords do not match') - return password - - if os.path.isfile(args.password): - with io.open(args.password, 'r', encoding='utf-8') as fp: - return fp.readline().strip() - - return args.password - - -def get_rnd_seed() -> str: - """ - Returns a random seed in the format: - - 2 random letters (capitalize()) + 5 random digits - """ - rnd = secrets.SystemRandom() - return ''.join( - rnd.choices(string.ascii_lowercase, k=2) - ).capitalize() + ''.join(rnd.choices(string.digits, k=5)) - - -def initiate_new_sequence(args: argparse.Namespace) -> str: - """ - Generates a new sequence based on the parameters sent from the parser. - - :param args: The arguments assigned from argparse - :type args: argparse.Namespace - - :raises otp2289.OTPChallengeException: If the challenge is invalid - - :raises otp2289.OTPGeneratorException: If generator parameters are wrong - - :return: The response string - :rtype: str - """ - if not args.seed: - args.seed = get_rnd_seed() - header = '' - if not args.quiet: - header = ( - f'Seed: {args.seed}, Step: {args.step}, ' - f'Hash: {args.hash_algo}{os.linesep}' - ) - generator = otp2289.generator.OTPGenerator( - args.password.encode(), - args.seed, - args.hash_algo, - ) - if args.challenge_string: - return header + generator.generate_otp_hexdigest_from_challenge( - args.challenge_string - ) - return header + generator.generate_otp_hexdigest(args.step) - - -def main(args=None): - """the main entry point""" - parser = argparse.ArgumentParser( - prog=__package__, - epilog=( - f'%(prog)s {otp2289.__version__} by Simeon Simeonov ' - '(sgs @ LiberaChat)' - ), - description='The following options are available', - ) - group = parser.add_mutually_exclusive_group(required=True) - password_group = parser.add_mutually_exclusive_group() - group.add_argument( - '--generate-otp-range', - action='store_true', - dest='generate_otp_range', - default=False, - help='Generates a range of OTP responses', - ) - group.add_argument( - '--generate-otp-response', - action='store_true', - dest='generate_otp_response', - default=False, - help='Generates a new OTP response', - ) - group.add_argument( - '--initiate-new-sequence', - action='store_true', - dest='initiate_new_sequence', - default=False, - help=( - 'Initiates a new OTP sequence. Essentially the same as ' - '--generate-otp-response only it prompts twice for password ' - 'and always outputs hex (ignores -f).' - ), - ) - password_group.add_argument( - '-P', - '--force-password-prompt', - dest='force_password_prompt', - action='store_true', - help=( - 'Force password prompt even if the env. variable ' - '"OTP2289_PASSWORD" is set' - ), - ) - password_group.add_argument( - '-p', - '--password', - metavar='', - type=str, - dest='password', - default='', - help=( - 'The password or path to password file ' - '(default & recommended: prompt for passwd)' - ), - ) - parser.add_argument( - '-a', - '--hash-algorithm', - metavar='', - type=str, - dest='hash_algo', - default='md5', - help='The hash algorithm to use. Possible values: md5 (default), sha1', - ) - parser.add_argument( - '-c', - '--challenge-string', - metavar='', - type=str, - dest='challenge_string', - default='', - help='Use challenge string when generating response', - ) - parser.add_argument( - '-f', - '--output-format', - metavar='', - type=str, - dest='output_format', - default='hex', - help='The output format to use. Possible values: hex (default), token', - ) - parser.add_argument( - '-i', - '--step', - metavar='', - type=int, - dest='step', - default=500, - help='The step. Default for initiating a new sequence is: 500', - ) - parser.add_argument( - '-q', - '--quiet', - action='store_true', - dest='quiet', - default=False, - help='Dot not show headers. Only hex / tokens', - ) - parser.add_argument( - '-r', - '--range', - metavar='', - type=int, - dest='range', - default=1, - help='Amount of consecutive OTP hex/tokens to generate. default: 1', - ) - parser.add_argument( - '-s', - '--seed', - metavar='[seed]', - type=str, - dest='seed', - default='', - help=( - 'The seed to use (1 to 16 alphanumeric characters) ' - '(default & recommended: random seed)' - ), - ) - parser.add_argument( - '-v', - '--version', - action='version', - version=f'%(prog)s {otp2289.__version__}', - help='display program-version and exit', - ) - args = parser.parse_args(args) - # handle the password before everything else - try: - args.password = get_password(args) - except KeyboardInterrupt: - eprint(os.linesep + 'Prompt terminated') - sys.exit(errno.EACCES) - except Exception as exp: - eprint(f'Unable to fetch password: {exp}') - sys.exit(1) - try: - if args.initiate_new_sequence: - print(initiate_new_sequence(args)) - if args.generate_otp_range: - print(generate_otp_range(args)) - if args.generate_otp_response: - print(generate_otp_response(args)) - sys.exit(0) - except otp2289.generator.OTPGeneratorException as exp: - eprint(f'GeneratorException: {exp}') - except otp2289.generator.OTPChallengeException as exp: - eprint(f'ChallengeException: {exp}') - except Exception as exp: - eprint(f'Unknown error: {exp}') - sys.exit(1) - - -if __name__ == '__main__': - main() -- cgit v1.3