Skip to content

aaronstanek/PythonPasswordUtility

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Python Password Utility

Cryptographically secure, easy-to-use, password generator

Most password generators written in Python make use of Python's secrets module to generate random values. Python Password Utility goes a step further: it chains together a keyed version of the SHA-512 (or SHA3-512) hash function to create a password which is computationally infeasible to guess, even for someone who knows the internal state of the secrets module.

Python Password Utility can be run directly from the command line, or called through a Python API.

This software requires Python 3.6 or later. Major versions of Python after Python 3 are not supported.

Index

  1. Description
  2. Installation
  3. Running from the Command Line
    1. Character Sets
    2. Advanced Examples of Character Sets
    3. Getting the Size of a Character Set
    4. Determining which Hashing Algorithm is being used
  4. Calling from Python
    1. generate_password
    2. charset_size
    3. SHA512_number

Description

The algorithm operates in a loop; at each iteration of the loop one character is added to the password. The program takes the "output state" of the previous iteration, and hashes it alongside the current time, 64 random bytes from Python's secrets module, a unique counter value, and the key. The result of this operation is the "output state" that will be used by the next iteration of the loop.

Each of the "output states" is then hashed again along with the time, a different 64 random bytes, and a different unique counter value. One byte is selected randomly from each of the resulting "candidate states" and is transformed into a password character.

The power of this technique comes from the keyed hash functions. For an attacker to have any hope of guessing a password generated in this way, they must first know the key which was used. If the user creates a new somewhat-random key for each password, and then removes all record of the key, it eliminates any chance of retroactively recovering the password. This holds true even in the absurdly unlikely case that an attacker knows both the time when the password was generated and the internal state of Python's secrets module.

Drawing randomness from multiple sources, and passing it through a SHA hash, guarantees that the resulting passwords will be patternless: knowing any part of the password will not give an attacker any information about the rest of the password. Passwords generated from Python Password Utility will have the maximum possible entropy for a password of their length and character set.

Installation

pip install passutil

Note: This software requires Python 3.6 or later. Major versions of Python after Python 3 are not supported.

Running from the Command Line

python -m passutil <valid_chars> <length> <random keyboard smashing, optional>

<valid_chars> tells the program which characters are permitted in your password. The format of this parameter is described below. z is generally the best option, unless there are restrictions on which characters may appear in the password. This parameter may not correspond to an empty set; to be able to generate a password, at least one character must be allowed to exist in the password. This parameter is case sensitive.

<length> is the desired length of the password. This can be any nonnegative integer.

<random keyboard smashing> is an optional parameter. The entire command is taken as the key for the hash function, so going wild on the keyboard after the <length> will increase the randomness of the generated password.

Character Sets

The basic character sets include:

  • u or c -> uppercase/capital: A-Z (26 characters)
  • l -> lowercase: a-z (26 characters)
  • n -> numeral: 0-9 (10 characters)
  • p -> ASCII punctuation: `~!@#$%^&*()-_=+[]{}\|;:'",<.>/? (32 characters)
  • r -> subset of p, some systems prohibit which punctuation characters may be used in a password, r omits those characters which are most likely to be banned, leaving: !@#$%&*()-_+[]{};:,.? (21 characters)
  • s -> the space character (1 character)
  • H -> uppercase hexadecimal characters: 0123456789ABCDEF (16 characters)
  • h -> lowercase hexadecimal characters: 0123456789abcdef (16 characters)

The names of the basic character sets are case sensitive; they must all be lowercase, other than H.

Example:

The command below will generate a password of length 4, only containing the numerals 0-9. It does this by specifying the character set n for numerals, and the length 4. The output might look like: 7014.

python -m passutil n 4

Example:

The command below will generate a password of length 25, only containing ASCII punctuation characters. It does this by specifying the character set p for punctuation, and the length 25. The output might look like: $/:<"/\~(+#/=";-:^(*)+'?-.

python -m passutil p 25

If we want to use characters from multiple character sets, we can perform a union on the sets simply by concatenating their names in the command.

Example:

The command below will generate a password of length 10, only containing numerals, spaces, and lowercase letters. It does this by combining the n, s, and l sets, to get the nsl set. The output might look like: p ltaj0k3p.

python -m passutil nsl 10

It is also possible to combine many character sets at once using shorthands:

  • a = ulnps, all the characters (95 characters)
  • ar = ulnrs, all the characters, except those which are likely to be banned (84 characters)
  • z = ulnp, all the characters, except spaces (94 characters)
  • zr = ulnr, all the characters, except spaces, and those which are likely to be banned (83 characters)

The shorthands are case sensitive; they must all be lowercase.

Example:

The command below will generate a password of length 16, containing any of the ASCII printable characters. It does this by using the a shorthand. The output may look like: ~VJ &FhC[_K8wu:+.

python -m passutil a 16

Note that the above command is fully equivalent to:

python -m passutil ulnps 16

Example:

The command below will generate a password of length 7, only containing uppercase letters, lowercase letters, numerals, restricted punctuation characters, and spaces. It does this by combining the zr shorthand with the s character set. The output may look like: 1A #T-x.

python -m passutil zrs 7

Note: zrs = szr = ulnrs = ar

(zr is expanded to its definition, and then merged with s)

However: zsr = ulnpsr = a

(z is expanded to its definition, the last r is ignored because it is already included in p)

Individual characters may be included with i or excluded with e.

Example:

The command below will generate a password of length 20, only containing numerals, A characters and b characters. It does this by first declaring the base set: n, indicating numerals, then modifying it with i. The Ab after the i signals that A characters and b characters are allowed in the password. The output may look like: 24b749b71A1669586098.

python -m passutil niAb 20

This feature is case sensitive; using uppercase I would not work here.

Example:

The command below will generate a password of length 18, only containing capital letters and ASCII punctuation. However, it will not contain the characters C, A, T, ., or /. It does this by first declaring the base set: cp, indicating capitals and punctuation, then modifying it with e. The CAT./ after the e signals that these characters may not appear in the password. The output may look like: [W*[*;{(UM@QL,_$XR.

python -m passutil cpeCAT./ 18

This feature is case sensitive; using uppercase E would not work here.

Some characters may need to be enclosed in parenthesis or prefixed with an escape backslash. See: https://www.shellscript.sh/escape.html

To include/exclude lowercase i or e, escape them with a .. prefix.

Example:

The command below will generate a password of length 14, only containing consonants in the English alphabet. It does this by first declaring the base set: l, indicating lowercase letters, then modifying it with e to exclude the vowel characters. Notice that in the exclusion, e and i are prefixed with .. to prevent them from being interpreted as the beginning of an exclusion or inclusion. The output may look like: hgydslgfzpsqft.

python -m passutil lea..e..iou 14

i and e may be used together

Example:

The command below will generate a password of length 26, only containing even numerals, a characters, and m characters. It does this by first declaring the base set: n, indicating numerals, then modifying it with i to include a and m characters. The following e will then exclude the 1, 3, 5, 7, and 9 characters from the password. The output may look like: m6am6a264m6m4m884m40aa6ma2.

python -m passutil niame13579 26

Example:

The command below will generate a password of length 7, only containing numerals, e characters, and excluding the numeral 5. It does this by first declaring the base set: n, indicating numerals, then modifying it with i to include e. Note that the first e is escaped with a .., and is therefore included by the preceding i, while the second e is not escaped, and so signals the start of an exclusion. 5 is excluded by the second e. The output may look like: 97739e8.

python -m passutil ni..ee5 7

Advanced Examples

Example:

IN:  python -m passutil li234 30
OUT: ppkksclllnihfghmrmxqklvcleiroe

Where's the 234 in the output?

The <valid_chars> parameter controls which characters may appear in the password, not which characters will appear in the password.

Example:

IN:  python -m passutil aen 30
OUT: x&deJD.PHG\]-5]0d\&!CX\wRwHyNl

I wanted to generate a password using all the characters, except for numerals. What's that 5 doing in there?

After an i or e starts an inclusion or exclusion, all subsequent characters (except for i and e) are interpreted literally.

Before any inclusions or exclusions, u, c, l, n, p, r, s, H, and h refer to sets of characters. (Definitions given above)

Within inclusions and exclusions, these same characters are interpreted as individual Latin alphabet letters to be included or excluded.

So, aen will generate a password using all available characters except for the lowercase letter n, which will be excluded. To generate a password using all the available characters except numerals, use ulps or more inconveniently ae0123456789.

Example:

IN:  python -m passutil iATCG 12
OUT: CACCCCCGTCTG

Declaring a base set is not required. The <valid_chars> parameter may begin with an i, followed by the characters to include.

Example:

IN:  python -m passutil ipeck 10
OUT: pppppppppp

What's going on here? I wanted a password using the letters p, e, c, and k, but I only get a bunch of p characters.

Because the e is not escaped, it is treated as the beginning of an exclusion. c and k are excluded, leaving only p in the character set.

To create a character set with the characters p, e, c, and k, the e needs to be escaped: ip..eck.

Example:

IN:  python -m passutil lebig 10
OUT: wdaozsgput

Why is there a g in the output? I wanted to exclude the characters b, i, and g.

Same thing as the previous example. i is not escaped. The correct character set will be produced with: leb..ig.

Example:

IN:  python -m passutil i..i 10
OUT: iiiiiiiiii

Wait, what?

The first i starts an inclusion. The escaped second i is the sole character that is included into the set.

Example:

IN:  python -m passutil e..e 10
OUT: valid_chars parameter has minimum size 1

Wait, what?

The first e starts an exclusion. No characters are added into the character set. The program needs at least one valid character to be able to produce a password.

Example:

IN:  python -m passutil ixiyey 10
OUT: xxxxxxxxxx

IN:  python -m passutil ixeyiy 10
OUT: xyyyxyyyxy

What's going on here? Why do such similar inputs produce such different outputs?

The generation of the character set operates from left to right.

In the first case: ix will add x to the set. iy will add y to the set. But then, ey will remove y from the set, leaving only x.

In the second case: ix will add x to the set. ey will try to remove y from the set, but as there is no y in the set, it will do nothing. Then, the program reaches iy, adding y to the set, resulting in a set containing both x and y.

Example:

Adding pseudorandomly generated keystrokes (e.g. pretending to type without actually making words) after the core of the command can help increase the randomness of the generated password. It is recommended that users make use of this feature to fully utilize the capacities of this software.

IN:  python -m passutil n 30 ju2ikntu i g rhtuirehtui3wh8r73hfsdnrrhtrthui
OUT: 834040642994017327433140007821

IN:  python -m passutil Hixyz 20 njfkern 4tio54n28r4 gmfd dginrekngtiosei8ufh
OUT: 18C29C2A9Az472BC1D27

IN:  python -m passutil zeI..i 5 min3w 12ji44tnf 4hNUFIWE 3U4ITR8H4RH283W7 fhu
OUT: Ss#oL

Getting the Size of a Character Set

Sometimes it is useful to know how many characters are in a character set before the character set is used. Python Password Utility allows one to do this from the command line.

python -m passutil --size <valid_chars>

<valid_chars> has the same format as above. However, unlike when generating a password, valid_chars may correspond to an empty set. In such a case, 0 is returned.

Example:

IN:  python -m passutil --size une5
OUT: 35

u has 26 characters. n has 10. e5 removes one character, leaving 35.

Hashing Algorithms

Python Password Utility generates passwords using either the SHA-2 512 algorithm or the SHA-3 512 algorithm.

To determine which algorithm is being used:

python -m passutil --hash

Calling from Python

Python Password Utility provides three publicly accessible objects.

generate_password

generate_password is a function. As the name suggests, it generates passwords in the same way that the command line interface does.

import passutil

password = passutil.generate_password(length, key, valid_chars)

length is a nonnegative int representing the desired number of characters in the password.

key is of type bytes or str. Its length cannot be zero. This value will be used to key the SHA hash function. For best results, use a different key value on each call to generate_password.

valid_chars indicates which characters may appear in the output password. It may be a str of the same format as the <valid_chars> parameter in the command line interface. Alternatively, valid_chars may be a set, list, or tuple containing permitted characters, encoded as single-character strings. Alternatively, valid_chars may be a set, list, or tuple containing codepoints for permitted characters. Only ASCII printable characters, or their codepoints, may exist in valid_chars. valid_chars may not correspond to an empty set.

The function will output a str containing the password.

Raises TypeError if length is not an int. Or if key is not a bytes or str object. Or if valid_chars is not a str, set, list, or tuple. Or if the contained elements are neither str nor int.

Raises ValueError if length is negative. Or if key has length zero. Or if the format is incorrect. Or if non-ASCII-printable characters are given in valid_chars.

Example:

import passutil
password = passutil.generate_password(4, "hello world", "n")
print(type(password)) # str
print(password) # 7014

This is analogous to the very first example given in this document.

Example:

import passutil
password = passutil.generate_password(12, b'\xf0\x9f\x98\x84', {"A","T",67,71})
# ASCII value 67 is "C", ASCII value 71 is "G"
print(password) # CACCCCCGTCTG

This is analogous to one of the advanced examples above.

charset_size

charset_size is the analog of --size in the command line interface. It is a function called in the following way:

import passutil

size = passutil.charset_size(valid_chars)

It returns an int. valid_chars has the same format as in generate_password. Unlike in generate_password, an input corresponding to an empty character set will not cause an exception; instead, the function will simply return 0, indicating a character set of size zero.

Raises TypeError if valid_chars is not a str, set, list, or tuple. Or if the contained elements are neither str nor int.

Raises ValueError if the format is incorrect or if non-ASCII-printable characters are given in valid_chars.

SHA512_number

SHA512_number is the analog of --hash in the command line interface. It is an int constant. A value of 2 indicates that SHA-2 512 will be used for hashing. A value of 3 indicates that SHA-3 512 will be used for hashing.

import passutil

algorithm_version = passutil.SHA512_number

About

Cryptographically secure, easy-to-use, password generator

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages