Skip to content

Commit

Permalink
Add calculation for password entropy
Browse files Browse the repository at this point in the history
Remove default password length, calculate it instead based on ENTROPY_BITS_MIN.
Warn when password is too short.
  • Loading branch information
HacKanCuBa committed Sep 22, 2017
1 parent e31717b commit f2fa1dc
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 22 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ It also makes use of the [EFF Large Wordlist](https://www.eff.org/es/document/pa

A secure passphrase must be of at least 6 words, but 7 is better, and maybe you can add a random number to the list. If you need a password, make it bigger than 8 characters (NIST's latest recommendation), and preffer more than 12 (I recommend 16 or more). Passwords are comprised of digits, upper and lower case letters and punctuation symbols - more specifically: `ascii_letters`, `digits` and `punctuation` from [Lib/string](https://docs.python.org/3.6/library/string.html#string-constants) -.

If you specify a list different than the EFF Large Wordlist, the minimum amount of words for a passphrase to be secure changes: for shorter lists, the amount increases. **Passphrase** calculates the minimum secure amount of words automatically and warns if the chosen number is low.
If you specify a list different than the EFF Large Wordlist, the minimum amount of words for a passphrase to be secure changes: for shorter lists, the amount increases. **Passphrase** calculates the minimum secure amount of words automatically and warns if the chosen number is low. It does the same for a password if it is too short.

## Requirements

For **Python 3.6+**:

* numpy 1.13+ [optional] for faster entropy computation
* flake8 [optional] for linting

For **Python 3.2+**:

* LibNaCl 1.5+
* numpy 1.13+ [optional] for faster entropy computation
* flake8 [optional] for linting

[passphrase.py](/src/passphrase.py) is a stand-alone, self contained script (the word list is embedded in it). It detects whether you have Python 3.6+ or lower, and acts accordingly. For Python 3.6+, it uses `Lib/secrets` (and is preferred); for Python 3.2+, `libnacl.randombytes_uniform`.
Expand Down Expand Up @@ -63,10 +65,10 @@ E`31nDL0^$oYu5='
#### Use an external wordlist to generate a passphrase

```
:~$ passphrase -i my-wordlist.txt
anguished estate placard deceptive entity
:~$ passphrase -d -i my-dicewarelike-wordlist.txt
unnamed unmanned appendix fineness riverside
:~$ passphrase -i eff_short_wordlist_1_1column.txt
wimp broke dash pasta zebra viral outer clasp
:~$ passphrase -d -i eff_short_wordlist_1.txt
mouse trend coach stain shut rhyme baggy scale
```

#### Save the output to a file
Expand Down
2 changes: 1 addition & 1 deletion man/passphrase.1
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ password, and prints it to standard output.
By default, it uses an embedded EFF Large Wordlist for passphrases.
Passphrases with less than 6 words are considered insecure. A safe bet is
between 6 and 7 words, plus at least a number.
For passwords, use at least 8 characters, but prefer 12 or more.
For passwords, use at least 12 characters, but prefer 16 or more.
.PP
Instead of words and numbers, a password (random string of printable
characters from Python String standard) can be generated by
Expand Down
4 changes: 2 additions & 2 deletions man/passphrase.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ Generates a cryptographically secure passphrase, based on a wordlist, or
a password, and prints it to standard output. By default, it uses an
embedded EFF Large Wordlist for passphrases. Passphrases with less than
6 words are considered insecure. A safe bet is between 6 and 7 words,
plus at least a number. For passwords, use at least 8 characters, but
prefer 12 or more.
plus at least a number. For passwords, use at least 12 characters, but
prefer 16 or more.

Instead of words and numbers, a password (random string of printable
characters from Python String standard) can be generated by **-p** |
Expand Down
38 changes: 24 additions & 14 deletions src/passphrase.py
Original file line number Diff line number Diff line change
Expand Up @@ -7796,13 +7796,12 @@
'zoom'
)

VERSION = '0.2.4'
VERSION = '0.2.5'

MAX_NUM = 999999
MIN_NUM = 100000
WORDS_AMOUNT_MIN_DEFAULT = 6 # Just for EFF's Large Wordlist
NUMS_AMOUNT_MIN_DEFAULT = 0
PASSWD_LEN_MIN_DEFAULT = 8 # From NIST's recomendation: http://bit.ly/2bpkyBc
ENTROPY_BITS_MIN = 77 # From EFF's post: http://bit.ly/2p96a2a


Expand Down Expand Up @@ -7928,6 +7927,9 @@ def generate_password(length: int) -> str:
import argparse
from sys import exit

# entropy_bits(list(characters)) = 6.554588
PASSWD_LEN_MIN_GOOD = ceil(ENTROPY_BITS_MIN / 6.554588)

parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description='Passphrase v{version} - Copyright HacKan '
Expand Down Expand Up @@ -7963,8 +7965,8 @@ def generate_password(length: int) -> str:
maxnum=MAX_NUM,
wordsamountmin=WORDS_AMOUNT_MIN_DEFAULT,
numsamountmin=NUMS_AMOUNT_MIN_DEFAULT,
passwdmin=PASSWD_LEN_MIN_DEFAULT,
passwdpref=PASSWD_LEN_MIN_DEFAULT + 4,
passwdmin=PASSWD_LEN_MIN_GOOD,
passwdpref=PASSWD_LEN_MIN_GOOD + 4,
version=VERSION
)
)
Expand All @@ -7986,7 +7988,7 @@ def generate_password(length: int) -> str:
"-p",
"--password",
type=bigger_than_zero,
const=PASSWD_LEN_MIN_DEFAULT,
const=PASSWD_LEN_MIN_GOOD,
nargs='?',
help="generate a password of specified lenght from all printable "
"characters"
Expand Down Expand Up @@ -8060,6 +8062,12 @@ def generate_password(length: int) -> str:
exit()

if passwordlen is not None:
if passwordlen < PASSWD_LEN_MIN_GOOD:
print_stderr(
"Warning: Insecure password length chosen! Should be bigger "
"than or equal to {}".format(PASSWD_LEN_MIN_GOOD)
)

passphrase = generate_password(passwordlen)
separator = ''
else:
Expand All @@ -8070,21 +8078,23 @@ def generate_password(length: int) -> str:
# Then: entropy_w * amount_w + entropy_n * amount_n >= ENTROPY_BITS_MIN
entropy_n = entropy_bits_nrange(MIN_NUM, MAX_NUM)
entropy_w = entropy_bits(words)
amount_w_e = (ENTROPY_BITS_MIN - entropy_n * amount_n) / entropy_w
amount_w_good_e = (ENTROPY_BITS_MIN - entropy_n * amount_n) / entropy_w
# print("entropy_n={}".format(entropy_n))
# print("entropy_w={}".format(entropy_w))
# print("amount_w_e={}".format(amount_w_e))
# print("amount_w_good_e={}".format(amount_w_good_e))

if amount_w_e > -1:
amount_w_default = ceil(abs(amount_w_e))
if amount_w_good_e > -1:
amount_w_good = ceil(abs(amount_w_good_e))
else:
amount_w_default = 0
amount_w_good = 0

if amount_w is None:
amount_w = amount_w_default
elif amount_w < amount_w_default:
print_stderr("Warning: Insecure amount of words chosen! Should be "
"bigger than or equal to {}".format(amount_w_default))
amount_w = amount_w_good
elif amount_w < amount_w_good:
print_stderr(
"Warning: Insecure amount of words chosen! Should be "
"bigger than or equal to {}".format(amount_w_good)
)

passphrase = generate(wordlist=words, amount_w=amount_w,
amount_n=amount_n)
Expand Down

0 comments on commit f2fa1dc

Please sign in to comment.