Mercurial > hgrepos > Python > apps > py-cutils
view cutils/genpwd.py @ 238:ff13b2a863ba
Make selection from a character repertoire more evenly distributed
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Sat, 08 Feb 2025 08:54:02 +0100 |
| parents | d65a4d861dad |
| children | 1eae57292c7c |
line wrap: on
line source
# -*- coding: utf-8 -*- # :- # :Copyright: (c) 2018 Franz Glasner # :Copyright: (c) 2025 Franz Glasner # :License: BSD-3-Clause # :- r"""Generate passwords. Usage: genpwd.py [ Options ] required_length Options: --type, -t web, web-safe, web-safe2, base64, base32, ascii85 :Author: Franz Glasner """ from __future__ import (division, absolute_import, print_function) import argparse import base64 import os import sys from . import (__version__, __revision__) WEB_CHARS = b"ABCDEFGHIJKLMNOPQRSTUVWYXZabcdefghijklmnopqrstuvwxyz" \ b"0123456789.,-_;!()[]{}*" WEB_SAFE_CHARS = b"ABCDEFGHJKLMNPQRSTUVWYXZabcdefghijkmnopqrstuvwxyz" \ b"23456789.,-_;!" WEB_SAFE2_CHARS = b".,-_;!" + WEB_SAFE_CHARS # prefer punctionation chars PY2 = sys.version_info[0] <= 2 def main(argv=None): aparser = argparse.ArgumentParser( description="A simple password generator for password of a given" " length within a character repertoire", fromfile_prefix_chars='@') aparser.add_argument( "--version", "-v", action="version", version="%s (rv:%s)" % (__version__, __revision__)) aparser.add_argument( "-E", dest="use_bin_length", action="store_true", help="For some repertoires make OUTPUT-LENGTH the number of bytes" " that is to be read from random sources instead of output bytes") aparser.add_argument( "--repertoire", "--type", "-t", choices=("web", "web-safe", "web-safe2", "base64", "urlsafe-base64", "urlsafe", "base32", "ascii85", ), default="web-safe2", help="Select the character repertoire. Default: web-safe2") aparser.add_argument( "req_length", metavar="OUTPUT-LENGTH", type=int, help="The required length of the generated output") opts = aparser.parse_args(args=argv) if opts.repertoire == "web": pwd = gen_web(opts.req_length, WEB_CHARS) elif opts.repertoire == "web-safe": pwd = gen_web(opts.req_length, WEB_SAFE_CHARS) elif opts.repertoire == "web-safe2": pwd = gen_web(opts.req_length, WEB_SAFE2_CHARS) elif opts.repertoire == "base64": encoder = base64.b64encode pwd = gen_bin(opts.req_length, opts.use_bin_length, encoder, rstrip_chars=b"=") elif opts.repertoire in ("urlsafe-base64", "urlsafe"): encoder = base64.urlsafe_b64encode pwd = gen_bin(opts.req_length, opts.use_bin_length, encoder, rstrip_chars=b"=") elif opts.repertoire == "base32": encoder = base64.b32encode pwd = gen_bin(opts.req_length, opts.use_bin_length, encoder, rstrip_chars=b"=") elif opts.repertoire == "ascii85": encoder = base64.a85encode pwd = gen_bin(opts.req_length, opts.use_bin_length, encoder) else: raise NotImplementedError("type not yet implemented: %s" % opts.repertoire) if opts.use_bin_length: if len(pwd) < opts.req_length: raise AssertionError("internal length mismatch") else: if len(pwd) != opts.req_length: raise AssertionError("internal length mismatch") if not PY2: pwd = pwd.decode("ascii") print(pwd) def gen_web(length, repertoire): assert len(repertoire) <= 256 pwd = [] while len(pwd) < length: rndbytes = os.urandom(16) for c in rndbytes: if PY2: c = ord(c) if c < len(repertoire): pwd.append(repertoire[c]) if len(pwd) >= length: break if PY2: pwd = b''.join(pwd) else: pwd = bytes(pwd) return pwd def gen_bin(length, use_bin_length, encoder, rstrip_chars=None): pwd = encoder(os.urandom(length)) return pwd.rstrip(rstrip_chars) if use_bin_length else pwd[:length] if __name__ == "__main__": main()
