Compare commits
8 Commits
ec3a51c23e
...
1c49155955
Author | SHA1 | Date | |
---|---|---|---|
1c49155955 | |||
a69deb4a5d | |||
|
8df520e6c8 | ||
|
4596fc38ee | ||
6795454af1 | |||
b34c41281a | |||
ff4f93a232 | |||
|
240e41712c |
37
README.md
37
README.md
@ -6,11 +6,10 @@ The functionality is heavily inspired by [HSXKPasswd](https://github.com/bbussch
|
|||||||
# Why
|
# Why
|
||||||
Because Randall Munroe says so.
|
Because Randall Munroe says so.
|
||||||
|
|
||||||
![alt text][xkcd]
|
![xkcd](https://imgs.xkcd.com/comics/password_strength.png "XKCD")
|
||||||
[xkcd]: http://imgs.xkcd.com/comics/password_strength.png
|
|
||||||
|
|
||||||
# Requirements
|
# Requirements
|
||||||
Python 2 or 3 as well as myspell-dictionaries for the language(s) you want to use.
|
Python 3 as well as my-/hunspell-dictionaries for the language(s) you want to use.
|
||||||
|
|
||||||
# What
|
# What
|
||||||
A password/passphrase generator. See below for some example usage.
|
A password/passphrase generator. See below for some example usage.
|
||||||
@ -21,25 +20,27 @@ $ sudo python3 setup.py install
|
|||||||
...
|
...
|
||||||
|
|
||||||
$ pwgen --help
|
$ pwgen --help
|
||||||
usage: pwgen [-h] [--generate-config] [--config-file CONFIG_FILE]
|
usage: pwgen [-h] [--quiet] [--generate-config] [--config-file CONFIG_FILE] [--myspell-dir MYSPELL_DIR] [--encoding ENCODING]
|
||||||
[--myspell-dir MYSPELL_DIR] [--lang LANG]
|
[--unmunch-bin UNMUNCH_BIN] [--lang LANG] [--word-min-char WORD_MIN_CHAR] [--word-max-char WORD_MAX_CHAR] [--words WORDS]
|
||||||
[--word-min-char WORD_MIN_CHAR] [--word-max-char WORD_MAX_CHAR]
|
[--capitalize {true,false,random}] [--separators SEPARATORS] [--trailing-digits TRAILING_DIGITS]
|
||||||
[--words WORDS] [--capitalize {true,false,random}]
|
[--leading-digits LEADING_DIGITS] [--special-chars SPECIAL_CHARS] [--trailing-chars TRAILING_CHARS]
|
||||||
[--separators SEPARATORS] [--trailing-digits TRAILING_DIGITS]
|
[--leading-chars LEADING_CHARS] [--passwords PASSWORDS] [--max-length MAX_LENGTH]
|
||||||
[--leading-digits LEADING_DIGITS] [--special-chars SPECIAL_CHARS]
|
|
||||||
[--trailing-chars TRAILING_CHARS] [--leading-chars LEADING_CHARS]
|
|
||||||
[--passwords PASSWORDS] [--max-length MAX_LENGTH]
|
|
||||||
|
|
||||||
Generate passwords
|
Generate passwords
|
||||||
|
|
||||||
optional arguments:
|
optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
|
--quiet, -q Only echo the generated passwords.
|
||||||
--generate-config, -g
|
--generate-config, -g
|
||||||
Generate configuration file and then exit
|
Generate configuration file and then exit
|
||||||
--config-file CONFIG_FILE, -c CONFIG_FILE
|
--config-file CONFIG_FILE, -c CONFIG_FILE
|
||||||
Configuration file to use
|
Configuration file to use
|
||||||
--myspell-dir MYSPELL_DIR, -i MYSPELL_DIR
|
--myspell-dir MYSPELL_DIR, -i MYSPELL_DIR
|
||||||
Directory containing myspell dictionaries
|
Directory containing myspell dictionaries
|
||||||
|
--encoding ENCODING, -e ENCODING
|
||||||
|
Character encoding of the directory
|
||||||
|
--unmunch-bin UNMUNCH_BIN, -u UNMUNCH_BIN
|
||||||
|
Path to my/hunspell unmunch binary
|
||||||
--lang LANG, -l LANG Dictionary language to use
|
--lang LANG, -l LANG Dictionary language to use
|
||||||
--word-min-char WORD_MIN_CHAR, -m WORD_MIN_CHAR
|
--word-min-char WORD_MIN_CHAR, -m WORD_MIN_CHAR
|
||||||
Minimum number of characters in a word
|
Minimum number of characters in a word
|
||||||
@ -58,17 +59,13 @@ optional arguments:
|
|||||||
--special-chars SPECIAL_CHARS, -S SPECIAL_CHARS
|
--special-chars SPECIAL_CHARS, -S SPECIAL_CHARS
|
||||||
Possible characters to use as extra special characters
|
Possible characters to use as extra special characters
|
||||||
--trailing-chars TRAILING_CHARS, -p TRAILING_CHARS
|
--trailing-chars TRAILING_CHARS, -p TRAILING_CHARS
|
||||||
Number of special characters to add at the end of the
|
Number of special characters to add at the end of the passphrase
|
||||||
passphrase
|
|
||||||
--leading-chars LEADING_CHARS, -P LEADING_CHARS
|
--leading-chars LEADING_CHARS, -P LEADING_CHARS
|
||||||
Number of special characters to add at the start of
|
Number of special characters to add at the start of the passphrase
|
||||||
the passphrase
|
|
||||||
--passwords PASSWORDS, -n PASSWORDS
|
--passwords PASSWORDS, -n PASSWORDS
|
||||||
Number of passwords to generate
|
Number of passwords to generate
|
||||||
--max-length MAX_LENGTH, -L MAX_LENGTH
|
--max-length MAX_LENGTH, -L MAX_LENGTH
|
||||||
Maximum length of the generated passwords. Full-
|
Maximum length of the generated passwords. Full-knowledge entropy calculation doesn't work when this is set.
|
||||||
knowledge entropy calculation doesn't work when this
|
|
||||||
is set.
|
|
||||||
|
|
||||||
$ pwgen -g --myspell-dir /usr/share/hunspell --lang sv_SE
|
$ pwgen -g --myspell-dir /usr/share/hunspell --lang sv_SE
|
||||||
Missing configuration file; generating a new at /home/user/.pwgen.cfg
|
Missing configuration file; generating a new at /home/user/.pwgen.cfg
|
||||||
@ -87,7 +84,3 @@ Blind entropy Password
|
|||||||
========================
|
========================
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
# TODO
|
|
||||||
* documentation
|
|
||||||
* webservice
|
|
||||||
|
35
bin/pwgen
35
bin/pwgen
@ -10,6 +10,10 @@ default_config_file=os.path.expanduser('~/.pwgen.cfg')
|
|||||||
def main():
|
def main():
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description='Generate passwords')
|
parser = argparse.ArgumentParser(description='Generate passwords')
|
||||||
|
parser.add_argument(
|
||||||
|
'--quiet', '-q',
|
||||||
|
action='store_true',
|
||||||
|
help='Only echo the generated passwords.')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--generate-config', '-g',
|
'--generate-config', '-g',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
@ -22,6 +26,12 @@ def main():
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--myspell-dir', '-i',
|
'--myspell-dir', '-i',
|
||||||
help='Directory containing myspell dictionaries')
|
help='Directory containing myspell dictionaries')
|
||||||
|
parser.add_argument(
|
||||||
|
'--encoding', '-e',
|
||||||
|
help="Character encoding of the directory")
|
||||||
|
parser.add_argument(
|
||||||
|
'--unmunch-bin', '-u',
|
||||||
|
help="Path to my/hunspell unmunch binary")
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--lang', '-l',
|
'--lang', '-l',
|
||||||
@ -79,6 +89,9 @@ def main():
|
|||||||
|
|
||||||
args = vars(parser.parse_args())
|
args = vars(parser.parse_args())
|
||||||
|
|
||||||
|
quiet = args['quiet']
|
||||||
|
del args['quiet']
|
||||||
|
|
||||||
config_file = args['config_file']
|
config_file = args['config_file']
|
||||||
del args['config_file']
|
del args['config_file']
|
||||||
|
|
||||||
@ -100,16 +113,20 @@ def main():
|
|||||||
|
|
||||||
pwds, seen_entropy = pwgen.generate_passwords(conf)
|
pwds, seen_entropy = pwgen.generate_passwords(conf)
|
||||||
|
|
||||||
print("Generated {} passwords".format(len(pwds)))
|
if (quiet):
|
||||||
if seen_entropy:
|
for pw in pwds.keys():
|
||||||
print("Full-knowledge entropy is {0:.2g}".format(seen_entropy))
|
print(pw)
|
||||||
else:
|
else:
|
||||||
print("Unable to calculate full-knowledge entropy since max_length is used")
|
print("Generated {} passwords".format(len(pwds)))
|
||||||
print("Blind entropy\tPassword")
|
if seen_entropy:
|
||||||
print("========================")
|
print("Full-knowledge entropy is {0:.2g}".format(seen_entropy))
|
||||||
for pw in pwds.keys():
|
else:
|
||||||
print("{:.5n}\t\t{}".format(pwds[pw]['entropy'], pw))
|
print("Unable to calculate full-knowledge entropy since max_length is used")
|
||||||
print("========================")
|
print("Blind entropy\tPassword")
|
||||||
|
print("========================")
|
||||||
|
for pw in pwds.keys():
|
||||||
|
print("{:.5n}\t\t{}".format(pwds[pw]['entropy'], pw))
|
||||||
|
print("========================")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
@ -1,21 +1,13 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import configparser
|
||||||
|
import locale
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
if sys.version_info[0] < 3:
|
import secrets as random
|
||||||
import ConfigParser as configparser
|
|
||||||
else:
|
|
||||||
import configparser
|
|
||||||
|
|
||||||
if sys.version_info >= (3, 6):
|
|
||||||
do_seed = False
|
|
||||||
import secrets as random
|
|
||||||
else:
|
|
||||||
do_seed = True
|
|
||||||
import random
|
|
||||||
|
|
||||||
class DictReadError(Exception):
|
class DictReadError(Exception):
|
||||||
pass
|
pass
|
||||||
@ -35,6 +27,8 @@ def update_config(
|
|||||||
lang='en_US',
|
lang='en_US',
|
||||||
word_min_char=2,
|
word_min_char=2,
|
||||||
word_max_char=0,
|
word_max_char=0,
|
||||||
|
unmunch_bin='',
|
||||||
|
encoding='',
|
||||||
|
|
||||||
words=4,
|
words=4,
|
||||||
capitalize='random',
|
capitalize='random',
|
||||||
@ -63,6 +57,8 @@ def update_config(
|
|||||||
set_if_defined(conf, 'dictionary', 'lang', lang)
|
set_if_defined(conf, 'dictionary', 'lang', lang)
|
||||||
set_if_defined(conf, 'dictionary', 'word_min_char', word_min_char)
|
set_if_defined(conf, 'dictionary', 'word_min_char', word_min_char)
|
||||||
set_if_defined(conf, 'dictionary', 'word_max_char', word_max_char)
|
set_if_defined(conf, 'dictionary', 'word_max_char', word_max_char)
|
||||||
|
set_if_defined(conf, 'dictionary', 'unmunch_bin', unmunch_bin)
|
||||||
|
set_if_defined(conf, 'dictionary', 'encoding', encoding)
|
||||||
|
|
||||||
if not conf.has_section('passwords'):
|
if not conf.has_section('passwords'):
|
||||||
conf.add_section('passwords')
|
conf.add_section('passwords')
|
||||||
@ -91,6 +87,9 @@ def _read_dictionary(conf):
|
|||||||
dict_file = os.path.join(conf.get('dictionary', 'myspell_dir'), '{}.dic'.format(conf.get('dictionary', 'lang')))
|
dict_file = os.path.join(conf.get('dictionary', 'myspell_dir'), '{}.dic'.format(conf.get('dictionary', 'lang')))
|
||||||
aff_file = os.path.join(conf.get('dictionary', 'myspell_dir'), '{}.aff'.format(conf.get('dictionary', 'lang')))
|
aff_file = os.path.join(conf.get('dictionary', 'myspell_dir'), '{}.aff'.format(conf.get('dictionary', 'lang')))
|
||||||
unmunch_bin = conf.get('dictionary', 'unmunch_bin')
|
unmunch_bin = conf.get('dictionary', 'unmunch_bin')
|
||||||
|
encoding = conf.get('dictionary', 'encoding')
|
||||||
|
if not encoding:
|
||||||
|
encoding = locale.getpreferredencoding(False)
|
||||||
words = set()
|
words = set()
|
||||||
chars = 0
|
chars = 0
|
||||||
if os.path.exists(aff_file) and unmunch_bin:
|
if os.path.exists(aff_file) and unmunch_bin:
|
||||||
@ -104,7 +103,7 @@ def _read_dictionary(conf):
|
|||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
raise DictReadError('Unmunching dictionaries failed')
|
raise DictReadError('Unmunching dictionaries failed')
|
||||||
for word in out.splitlines():
|
for word in out.splitlines():
|
||||||
save = word.strip().decode('utf-8')
|
save = word.strip().decode(encoding)
|
||||||
if not save:
|
if not save:
|
||||||
continue
|
continue
|
||||||
first_char = save[:1]
|
first_char = save[:1]
|
||||||
@ -120,7 +119,7 @@ def _read_dictionary(conf):
|
|||||||
words.add(save)
|
words.add(save)
|
||||||
chars += len(save)
|
chars += len(save)
|
||||||
else:
|
else:
|
||||||
with open(dict_file, 'r') as f:
|
with open(dict_file, encoding=encoding, mode='r') as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
if not line:
|
if not line:
|
||||||
continue
|
continue
|
||||||
@ -153,8 +152,6 @@ def generate_passwords(conf):
|
|||||||
capitalize = conf.get('passwords', 'capitalize')
|
capitalize = conf.get('passwords', 'capitalize')
|
||||||
max_len = conf.getint('passwords', 'max_length')
|
max_len = conf.getint('passwords', 'max_length')
|
||||||
|
|
||||||
if do_seed:
|
|
||||||
random.seed()
|
|
||||||
res = {}
|
res = {}
|
||||||
dict_data = _read_dictionary(conf)
|
dict_data = _read_dictionary(conf)
|
||||||
|
|
||||||
|
9
setup.py
9
setup.py
@ -8,24 +8,23 @@ except ImportError:
|
|||||||
|
|
||||||
import pwgen
|
import pwgen
|
||||||
|
|
||||||
version = '0.1'
|
version = '1.0'
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='pwgen',
|
name='pwgen',
|
||||||
version=str(version),
|
version=str(version),
|
||||||
description="Passphrase generator",
|
description="Passphrase generator",
|
||||||
author="Fredrik Eriksson",
|
author="Fredrik Eriksson",
|
||||||
author_email="pwgen@wb9.se",
|
author_email="feffe@fulh.ax",
|
||||||
url="https://github.com/fredrik-eriksson/pwgen",
|
url="https://gitea.fulh.ax/feffe/pwgen",
|
||||||
platforms=['any'],
|
platforms=['any'],
|
||||||
license='BSD',
|
license='BSD',
|
||||||
packages=['pwgen'],
|
packages=['pwgen'],
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 1 - Planning',
|
'Development Status :: 5 - Production/Stable'
|
||||||
'Environment :: Console',
|
'Environment :: Console',
|
||||||
'Intended Audience :: System Administrators',
|
'Intended Audience :: System Administrators',
|
||||||
'License :: OSI Approved :: BSD License',
|
'License :: OSI Approved :: BSD License',
|
||||||
'Programming Language :: Python :: 2',
|
|
||||||
'Programming Language :: Python :: 3',
|
'Programming Language :: Python :: 3',
|
||||||
'Topic :: Utilities',
|
'Topic :: Utilities',
|
||||||
],
|
],
|
||||||
|
Loading…
Reference in New Issue
Block a user