Skip to content

Commit

Permalink
Make it version independent, implement LibNaCl
Browse files Browse the repository at this point in the history
Add requirement, edit README accordingly.

PyNaCl didn't have a cryptographically secure way to generate integers,
so I dropped it out and implemented LibNaCl that comes with Libsodium's
randombytes_uniform().
  • Loading branch information
HacKanCuBa committed Sep 21, 2017
1 parent 3273aca commit beaec46
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 28 deletions.
23 changes: 17 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Passphrase

**Passphrase** is a tool to generate **cryptographically secure** passphrases and passwords. It's currently based on the security of Python's [Lib/secrets](https://docs.python.org/3/library/secrets.html#module-secrets), both passphrases and passwords are securelly generated using `secrets.choice()`:
**Passphrase** is a tool to generate **cryptographically secure** passphrases and passwords.

For **Python 3.2+**, it's currently based on the security of [LibNaCl's](https://github.com/saltstack/libnacl) [randombytes_uniform](https://download.libsodium.org/doc/generating_random_data/#usage), both passphrases and passwords are securelly generated using `libnacl.randombytes_uniform()`.

For **Python 3.6+**, it's currently based on the security of Python's [Lib/secrets](https://docs.python.org/3/library/secrets.html#module-secrets), both passphrases and passwords are securelly generated using `secrets.choice()` and `secrets.randbelow()`:

> The `secrets` module is used for generating cryptographically strong random numbers suitable for managing data such as passwords, account authentication, security tokens, and related secrets.
Expand All @@ -10,17 +14,25 @@ A secure passphrase must be of at least 5 words, but 7 is better, and maybe you

## Requirements

* Python 3.6
For **Python 3.6+**:

* flake8 [optional] for linting

For **Python 3.2+**:

* LibNaCl 1.5+
* flake8 [optional] for linting

[passphrase.py](/src/passphrase.py) is a stand-alone, self contained (the word list is embedded in it) script, and has no requirements besides Python 3.6 (because the `secrets` module is present since that Python version). I'm planning to implement PyNaCl so it can be used with Python 3.x.
[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`.

## How to use it

Just download the script, preferrably fom the [latest release](/releases/latest) - releases are always signed - and give it execution permission. It can be run as `:~$ python3.6 src/passphrase.py`, or if you copy it to /usr/local/bin (system-wide availability) or ~/.local/bin (user-wide availability), as `:~$ passphrase`.
Just download the script, preferrably fom the [latest release](https://github.com/HacKanCuBa/passphrase-py/releases/latest) - releases are always signed - and give it execution permission. It can be run as `:~$ python3.6 src/passphrase.py`, or if you copy it to /usr/local/bin (system-wide availability) or ~/.local/bin (user-wide availability), as `:~$ passphrase`.

You can use `make install` to install it system-wide (requires root or `sudo`) or `make altinstall` for user-wide. Installing it simply copies the script to destination along with the man page.

To install requirements, use pip: `pip3 install -r requirements.txt`.

### Examples of use

Check the [man page](man/passphrase.md) for more information.
Expand Down Expand Up @@ -74,7 +86,7 @@ gpg: encrypted with 1 passphrase
589ed823e9a84c56feb95ac58e7cf384626b9cbf4fda2a907bc36e103de1bad2 -
```

### Generate a passphrase avoiding [shoulder surfing](https://en.wikipedia.org/wiki/Shoulder_surfing_(computer_security))
#### Generate a passphrase avoiding [shoulder surfing](https://en.wikipedia.org/wiki/Shoulder_surfing_(computer_security))

```
:~$ passphrase -q -o pass.txt
Expand All @@ -98,4 +110,3 @@ gpg: encrypted with 1 passphrase

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
libnacl~=1.5
flake8~=3.4
76 changes: 54 additions & 22 deletions src/passphrase.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
"""Generates passphrases based on a word list using cryptographically secure
random number generator"""

from string import digits, ascii_letters, punctuation
from sys import stderr, version_info
from os.path import isfile

import os
import secrets
from sys import stderr
assert (version_info >= (3, 2)), "This script requires Python 3.2+"

# EFF large wordlist
WORDS_DEFAULT = (
Expand Down Expand Up @@ -7793,7 +7794,7 @@
NUMS_AMOUNT_MIN_DEFAULT = 0
PASSWD_LEN_MIN_DEFAULT = 8

VERSION = '0.2.1'
VERSION = '0.2.3'


def print_error(string: str) -> None:
Expand All @@ -7815,23 +7816,6 @@ def read_words_from_diceware(inputfile: str) -> list:
return words


def generate(wordlist: list, amount_w: int, amount_n: int) -> list:
passphrase = []
for i in range(0, amount_w):
passphrase.append(secrets.choice(wordlist))

for i in range(0, amount_n):
passphrase.append(secrets.randbelow(MAX_NUM))

return passphrase


def generate_password(length: int) -> str:
import string
characters = string.digits + string.ascii_letters + string.punctuation
return ''.join(secrets.choice(characters) for i in range(0, length + 1))


def bigger_than_zero(value: int) -> int:
ivalue = int(value)
if ivalue < 0:
Expand All @@ -7841,6 +7825,54 @@ def bigger_than_zero(value: int) -> int:
return ivalue


if version_info >= (3, 6):
# Use Lib/secrets
from secrets import choice, randbelow

def generate(wordlist: list, amount_w: int, amount_n: int) -> list:
passphrase = []
for i in range(0, amount_w):
passphrase.append(choice(wordlist))

for i in range(0, amount_n):
passphrase.append(randbelow(MAX_NUM))

return passphrase

def generate_password(length: int) -> str:
characters = digits + ascii_letters + punctuation
return ''.join(choice(characters) for i in range(0, length + 1))


else:
# Use libnacl
from libnacl import randombytes_uniform

def generate(wordlist: list, amount_w: int, amount_n: int) -> list:
passphrase = []
index = None
num = None
for i in range(0, amount_w):
index = randombytes_uniform(len(wordlist))
passphrase.append(wordlist[index])

for i in range(0, amount_n):
num = randombytes_uniform(MAX_NUM)
passphrase.append(num)

return passphrase

def generate_password(length: int) -> str:
characters = digits + ascii_letters + punctuation
passwd = []
index = None
for i in range(0, length + 1):
index = randombytes_uniform(len(characters))
passwd.append(characters[index])

return ''.join(passwd)


if __name__ == "__main__":
import argparse
from sys import exit
Expand Down Expand Up @@ -7966,7 +7998,7 @@ def bigger_than_zero(value: int) -> int:

if inputfile is None:
words = WORDS_DEFAULT
elif os.path.isfile(inputfile) is True:
elif isfile(inputfile) is True:
if is_diceware is True:
words = read_words_from_diceware(inputfile)
else:
Expand Down

0 comments on commit beaec46

Please sign in to comment.