Password card

last update: 2 Feb 2023

Abstract

This notebook describes the use of a password card and the Python code that generates the password card. The password card is a grid of random letters, numbers and special characters that provides a convenient aid to generate and remember long and complex passwords. A basic description of the password card and instructions are provided along with the Python code used to generate the password card. A security analysis and an analysis of alternatives is provided.

Motivation

Coming up with secure passwords and remembering a unique password for each account is a challenge. The safest way to store your passwords is to memorize them, but for long and complex passwords this is not practical. Passwords that humans can remember and use are guessable. The password card described in this notebook is a paper card that once generated is off-line, an independent record of your passwords and is protected by a secret word that you memorize. In one sense, the password card is a Password manager, but it is not computerized.

Introduction

This notebook describes the generation and use of a password card similar to the card called “C@RD”, a laminated card sold by Russtopia Labs for $4.25. The password card provides an easy way to generate and remember long and complex passwords. The version of the card described here has the upper case letters of the alphabet printed on line number one of the card. The card consists of an array of letters, numbers and special characters that can be used as passwords for web pages and can be printed on a credit-card sized piece of paper which can be laminated and carried in your wallet or pocket. A sample card is shown below:

The first three rows are the upper case letters of the alphabet, the lower case letters, the special characters and numbers used on the card. All the characters used in the passwords are printed here as a reference since for some fonts, some characters are hard to distinguish, for example, l and 1. Rows four through nine are random combinations of the letters of the alphabet, numbers and special characters. The columns of rows four through nine contain the characters used in the passwords and each column contains at least one uppercase letter, one lowercase letter, one digit and one special character in addition to two randomly selected characters, for a total of six characters. The last row on the card is a random sequence of numbers that can be used for numerical PINs.

The passwords for your accounts are generated from two parts, the first part of your password comes from the card, the second part is a secret word, committed to memory, consisting of a combination of letters, numbers or special characters that only you know. When combined, the combination yields passwords which are complex and of adequate length for most applications.

Good passwords have the following characteristics: - They do not contain personal information or common words - They do not contain words or phrases from songs lyrics, poetry, literature etc. - They are long enough to resist brute force guessing attacks from current and future password cracking systems - They are not from a list of previously compromised passwords e.g. names, names with numbers or symbols, words from dictionaries etc. - They do not contain simple substitutions such as @ for a, 5 or $ for s, 1 for i, etc.

Which implies that the password should be random, not easily guessed and as a result can be hard to memorize.

Unique passwords for each account

Each account should have a unique password. If the same password is used for multiple accounts, then all accounts using that same password are vulnerable if your reused password is compromised. This advice is almost universally given by security experts and journalists who write on the subject. Credential stuffing is a type of cyber attack in which the attacker collects stolen account credentials, typically consisting of lists of usernames and/or email addresses and the corresponding passwords (often from a data breach), and then uses the credentials to gain unauthorized access to user accounts on other systems through large-scale automated login requests directed against a web application. Unlike credential cracking, credential stuffing attacks do not attempt to use brute force or guess any passwords – the attacker simply automates the logins for a large number (thousands to millions) of previously discovered credential pairs using standard web automation tools. The best way to protect against credential stuffing is to use unique passwords on accounts.

Other password cards

The following are links to other types of password cards. - PasswordCard: The user picks a row and column and reads the password in the direction chosen. Could be left, right or diagonal. - Password card generator: You can pick on a row, a column, click on random characters and/or draw images on your Password Card that are easy to remember. - PassCard: similar to PasswordCard above - PasswordWrench: similar to Password card generator above - Qwertycards: the password is a character substitution cipher of the web site’s name.

Canadian patent

The “C@RD” from Russtopia Labs was designed and patented in Canada and was protected under Canadian patent number 2,895,597. In 2016 and 2019 the patent expired due to failure to respond and failure to pay application maintenance fee.

Password card instructions

Instructions for the use of the card are provided by Russtopia Labs and are summarized here.

  1. Choose a secret word or phrase known only to you. This ensures that even if your password card is lost or stolen, no one will know the full password you generate from the card.
  2. Using the first two letters of the website or name of the company holding your account, choose the appropriate columns and read downwards in rows four through six. Every column has at least one letter (both upper- and lower-case), a digit and a special character.

The image below, shows the card with sections highlighted in colored boxes.

Passwords are generated by using the first two letters of the website or company holding your account and then reading down the columns of rows four through six. If the web site is amazon.com, choose columns “A” and “M”, and read the characters from rows four through six. These are highlighted in the black and blue boxes. In column “A” (in blue), the characters are “8@%yP!”, and in column “M” (in black), the characters are “kfoC?2”. If your personal secret word is “apple”, then the full password for your amazon account would be “8@%yP!kfoC?2apple”, which is a complex 17 character password containing upper and lower case letters, digits and special characters. Since you know the procedure, you don’t have to remember the 17 characters, just use the procedure along with your card to reconstruct your password.

Rotating passwords

For rotating passwords, add two characters from rows four and five of numbered columns in row 3, (01=Jan), (02=Feb), for each month. (eg., in row four, two characters in column for 1st month digit, then two characters in column for 2nd month digit, February=02, under 0 in row four, read “5e”. Then under 2 in row five, read “dA” and prefix to password. The resulting password would be “5edA” plus the remaining base password, “5edA8#&yP!kfoC?2apple”. In three months or whenever the password expires, use the month to generate a new prefix. This is the procedure outlined by Russtopia Labs.

Some sites will check to see if the new password is sufficiently different than, say for example, the last ten passwords used. If the passwords are stored as a hashed value, then any difference, even one character difference would be sufficient. But if the prior passwords are stored as plain text or in an encrypted file, the system could check the degree of difference between old passwords and the new one. So if the site is enforcing a new password that is different from your old password by some amount of characters, then the following procedure can be used.

At the beginning of the year, in January for example, if a new password is required, construct the new password as follows: - The months are numbered from 1 to C as shown in the table below and the characters are from the column under that month number or letter. So in January, month number 1, use the characters from column 1 which are: 3bXz%f. - The next characters are from column A and M (for Amazon), but only three characters from each column are used: A = 8@% and M = kfo. - Then append your secret word.

month col # col chars A col M col secret word
Jan 1 3bXz%f 8@% kfo apple
Feb 2 dAk)$1 8@% kfo apple
Mar 3 ?y(1?J 8@% kfo apple
Apr 4 *-1@Ui 8@% kfo apple
May 5 66!A$b 8@% kfo apple
Jun 6 &jcPQ6 8@% kfo apple
Jul 7 mrc3B# 8@% kfo apple
Aug 8 ><^nJ7 8@% kfo apple
Sep 9 5DktW< 8@% kfo apple
Oct A 8@%yP! 8@% kfo apple
Nov B X4Mz+b 8@% kfo apple
Dec C ax!p2M 8@% kfo apple

A new password for January would be: 3bXz%f8@%kfoapple. In April, if the password has to be changed, it would be: *-1@Ui8@%kfoapple. You only need to remember in which month you created the new password.

Numeric PIN

For a numeric PIN, pick a letter and start with that number in the random number row. For example, if your bank is Commerce Bank, under the letter “C” in the first row, the PIN numbers are “141845” (red highlight). You can use four numbers if you want.

For sites that restrict the number of characters in the password, choose a 4-letter secret word and only read the first two or three symbols from each column in Step 2) above. This will give you an 8 or 10 character password to meet password restrictions of old systems.

Restricted characters

For sites with restrict the use of special characters, read the columns as usual but each time you encounter a prohibited special character, use a letter instead (for example, “<” becomes “n”, “>” becomes “o” etc.) which are the letters in row two directly above the special characters in row three.

If the website or account has repeated letters, for example “aa.com”, this would result in using the same column “a” twice, which according to the original procedure is acceptable. However if you feel uncomfortable using a password with any repeated sequences, shift to the left or right adjacent column for the second letter – in essence treating “aa” as “ab” (second “a” becomes “b”).

Typically, a user would keep a list of web page names and the corresponding user name, web site letter code and notes. The user would refer to this list for websites that are not often used or have restricted characters. The user would refer to the list until it became committed to memory.

Secret word selection

The purpose of the secret word is to keep at least part of your password secret in the event that someone finds your password card. The secret word part is memorized and not written down anywhere. In the examples above, the secret word used was “apple”. You should use something different.

There are about 255 thousand defined words in the Merriam-Webster’s Collegiate Dictionary English dictionary, choosing one random word out of a dictionary provides about 17 bits of entropy. Your secret word should have at least 30 bits of entropy, in order to prove a modest level of security. One random word would provide a bit of security that might keep a nosy family member out of your email account if they “borrowed” your password card. Two random words concatenated would provide about 35 bits of entropy, enough to keep even the most determined family member out of your email account. For example the two words, “program” and “blind” could be concatenated to generate a secret passphrase, “programblind”.

Multiple random words concatenated is called a passphrase. The more words that are in the passphrase, the more secure it is, but harder to memorize. To prevent personal bias from affecting your choice of words you could use dice to aid in choosing words from the dictionary. Roll some dice and choose a dictionary page number based on the dice rolls. Roll the dice some more and choose a word on the page. Repeat until you have the number of words you want. One way to harden short passphrases is to mangle the passphase, for example “programblind” could be mangled to be “pRogram=bli2nd”. Not impossible to memorize, but nearly impossible to guess. With the password card, “pRogram=bli2nd” is the only part you need to commit to memory.

Python Code

This section of the notebook contains the Python code used to generate the password card. The Python library Secrets is used to randomly choose elements from the character set. 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.

The Python code described below can be used to generate a unique card for each person.

import math
import sys
import re
import hashlib
import secrets
gen = secrets.SystemRandom()
from collections import Counter
import matplotlib.pyplot as plt
from sympy.solvers import solve
from sympy import Symbol

Character set

The following characters are used in the password card. Upper and lowercase letters of the alphabet, the digits 0 to 9, as well as the so-called special characters are used. A special character is one that is not considered a number or letter. Special characters are often required when creating a strong password. This is because they add complexity to the password. There are 33 characters classified as ASCII punctuation and symbols are also sometimes referred to as ASCII special characters.

The special characters used in this notebook are: !@#$%^&*()-+=<>?

However, other characters from the keys on a typical US standard keyboard could also be used: ~!@#$%^&*()_+-=,./<>?;’:“[]{}|
Any of these could be easily substituted to give the password card additional variation. Up to 16 special characters will fit into the scheme.

lower = 'abcdefghijklmnopqrstuvwxyz'
upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
digits = '0123456789'
special = '!@#$%^&*()-+=<>?'
char_set_size = len(upper)+len(lower)+len(digits)+len(special)
print('character set size: {:d}'.format(char_set_size))
character set size: 78

Set the number of rows

The number of rows in the random part of the password card is set to 6, as in the original card. Variations of the password card could be made with a different number of rows.

num_rows = 6

Generate the password random characters

Every column has one upper, one lower, one digit and one special character, followed by two random characters from the complete set. And when two columns are used, the password has at least two lower case, two upper case, two digits and two special characters.

A=[]    #make empty list for random characters
for j in range(26):
    temp = '' # make an empty character string
    temp += secrets.choice(lower)
    temp += secrets.choice(upper)
    temp += secrets.choice(digits)    
    temp += secrets.choice(special)      
    # add more random characters
    for i in range(num_rows-4):
        temp += secrets.choice((lower + digits + upper + special))  
    # take the list and randomize the order
    temp = secrets.SystemRandom.sample(gen, temp, num_rows)
    A.append(temp)

#printing the rows of the password card
for i in range(num_rows):
    for j in range(len(lower)):
        print(A[j][i],end='') # end='' does not print new line for each call to print
    print()
R*^ahl4G1abF@3cfG&V2i$3y65
8q3R>D&-0R?#2L@Ve*X<qo>22J
4%V^W24uv1)4@uKP1Pp16YKKZu
&7Pv1Yl6%JY2V%3(>24i$?hH9t
o7czU(E0ZDUx<G@8own8T0q1w0
BE94v5Rw6%0DiAMF?K#BZ5W?@?

The block of text can be copied into a document to save the results. Every time the code above is run, a new block of random characters is generated.

Random row of digits

The last row in the password card is a row of random digits which can be used for PIN’s.

#print a row of random numbers
temp = ''
for i in range(26):
    temp += secrets.choice(digits)
print(temp)
71073634961270785886390837

Copy the rows of the password card to a word document for formatting and printing. The card works best if every other column is highlighted to make reading easier.

ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
!@#$%^&*()-+=<>?0123456789
)0A4+^ZYwAk17%21?33F(NOo94
72!5dFZzgCN^t9@oqiISqlq1Da
jDbVqD3N-4<yiInG!c-0ddW$rR
U!8!U5c0X>soys*)*Zx?M-?I?#
bE7l7m$b9o0E&%OoFVWh$fwI%@
EvyR-<f>?)qQD*hW2)V9457$@N
85640243163554984484266573

Secret word

The secret word is the part of your password that you commit to memory. It doesn’t change, you only have to memorize this part. In the example the secret word is “apple”, but as discussed above is not sufficient. The following code uses simulated dice rolls to choose two random words from a list such as list of English words (479k words) to construct a pass phrase and then mangles the passphrase to defend against dictionary attacks.

The word list file is read in and filtered by length. Words with spaces, all caps and punctuation are also removed from the list.

words = []
min_length = 5
max_length = 9
valid_chars = '.'
regexp = re.compile("^{0}{{{1},{2}}}$".format(valid_chars,min_length,max_length))

# read words from file into wordlist
with open('wordlist.txt') as wlf:
    for line in wlf:
        thisword = line.strip()
        if regexp.match(thisword) is not None:
            words.append(thisword)

# remove words with spaces, all caps, and words with punctuation
temp_list = []
for i in words:
    if not i.isupper() and i.isalpha():
        temp_list.append(i)

The words in the list are shuffled since the selection later is somewhat Gaussian and would tend to pull words from the middle of the list.

wordlist = []
while len(temp_list) > 0:
    picked_item = secrets.choice(temp_list)
    wordlist.append(picked_item)
    temp_list.remove(picked_item)
# number of bits to use in the index
num_bits = 15
# check to see if word list is long enough
len(wordlist) > 2**num_bits
True

The fill list is trimmed so that the length is equal to \(2^{\text{number of bits}}\)

abridged_wordlist = wordlist[0:2**num_bits]
print('number of words in the abridged word list: {:,.0f}'.format(len(abridged_wordlist)))
number of words in the abridged word list: 32,768

The first 150 words in the abridge_wordlist are:

i=0
for j in range(10):
    for k in range(15):
        print(abridged_wordlist[i]+' ',end='')
        i += 1
    print()
hashish finch backboard Penates respected coryza forge tillable spoke ripost bocci cycling cicatrice grasping sackful 
coveralls incense nerve Dempsey magnetise notchback niece doubly nobly promenade perfervid debit Burnett Paleozoic syndrome 
precursor occiput fishhook Hitlerian superman seconds mayfly lightener reveille conga celled geyser diverse midsize lunkhead 
ventricle devoted screed detest starless cosign tactility sturdy caliph Dwaine spearmint smudgy thrift philter adore 
inebriety addictive tensely Croatian printout waxwork frostily sweat Nikolayev paleness tinker creosote offish Dotty ferryboat 
warped snoopily roughhewn Graham bodacious rancour gazpacho Wankel bamboozle staggers souvenir sobering fiver incarnate pickling 
singer unweave cellaret Sheridan wiggler exact beverage august insomuch gorgeous sweating looniness inciter affirm reflexly 
schwa picot boozing whitebait fistfight edacity unfilled exemplify ambler Lavern teaching guide battery greenness valiantly 
scamperer adoptee Everest backcourt oxygenous Colombia purposely Myrtle lighted benzoate volcanism alveolus surrey centigram Okinawa 
ingenuous gradient striving regicide solidus setback anarchist yardman dollish decillion Fritz Wabash uncombed Croce unloader 

As shown above the words in the abridge list are not in alphabetical order.

print('the abridged list includes words from "{:s}" to "{:s}"'.format(min(abridged_wordlist),max(abridged_wordlist)))
the abridged list includes words from "Aalborg" to "zymurgy"

The following code simulates dice rolls. Actual dice could be used and the results entered into the code below.

num_rolls = 50
print('{:d} dice rolls have an entropy of: {:.0f} bits'.format(num_rolls, math.log2(6**num_rolls)))
50 dice rolls have an entropy of: 129 bits
# generate n dice rolls
rd_str = ''
for i in range(num_rolls):
    rd_str += secrets.choice('123456')

print('the string of {:d} dice rolls: {:s}'.format(num_rolls,rd_str))
the string of 50 dice rolls: 25666355551553246453252641525155213415543612634242

Physical dice can be rolled and the digits copied into the string variable.

Dice value frequency

The following graph checks the dice value frequency, it should be somewhat uniform and there should be no missing digits. This is a useful graph if you are rolling physical dice and entering the results into the program.

# using collections.Counter() to get a count of the occurrence of each digit in string  
mgram = Counter(rd_str) 

sorted_dict = {key: value for key, value in sorted(mgram.items())}

plt.bar(range(len(sorted_dict)), sorted_dict.values(), align='center')
plt.xticks(range(len(sorted_dict)), list(sorted_dict.keys()))
plt.show()

Using the above graph to check the distribution of digits, which should appear somewhat uniform and at least there should be no missing digits. The list of dice rolls is converted into an integer by hashing the string, that way all the dice rolls are used.

m = hashlib.sha256()
m.update(rd_str.encode('utf-8'))
print('the hashed value of the dice roll string: {:s}'.format(m.hexdigest()))
the hashed value of the dice roll string: 77733b54a98eca78d68e5a68ddd309e7b5aa716b76f8202410f27d460a76e171
rn_int = int(m.hexdigest(),base=16)
print('convert the hashed value into an integer: {:d}'.format(rn_int))
print('length of digit string: {:d} characters'.format(len(str(rn_int))))
convert the hashed value into an integer: 54028825879384713162013971062835122052298057344052197559299222554916219707761
length of digit string: 77 characters

Digit frequency

The following graph displays the digit frequency of the converted integer.

# using collections.Counter() to get a count of the occurrence of each digit in string  
mgram = Counter('{:d}'.format(rn_int)) 

sorted_dict = {key: value for key, value in sorted(mgram.items())}

plt.bar(range(len(sorted_dict)), sorted_dict.values(), align='center')
plt.xticks(range(len(sorted_dict)), list(sorted_dict.keys()))
plt.show()

print('number of bits in the number: {:d}'.format(len('{:b}'.format(rn_int))))
number of bits in the number: 255
# number of words to use in the pass phrase
num_words = 2

The words in the word list are indexed by taking 15 bits at a time from the number generated from the hashed dice string.

print('number of words that can be indexed with hashed value: {:.0f}'.format(len('{:b}'.format(rn_int))/num_bits))
number of words that can be indexed with hashed value: 17
pass_phrase = ''
for i in range(num_words):
    pass_phrase += abridged_wordlist[(rn_int >> num_bits*i) & 2**num_bits-1]
    print('{:2d}: {:5d} = {:s}'.format(i+1, (rn_int >> num_bits*i) & 2**num_bits-1, abridged_wordlist[(rn_int >> num_bits*i) & 2**num_bits-1]) )
print(pass_phrase)
 1: 24945 = apologize
 2:  5357 = modishly
apologizemodishly

Mangle the passphrase to defend against dictionary attacks.

Check for any caps, if not add one in a random place

if pass_phrase.islower():
    i = secrets.randbelow(len(pass_phrase))
    pass_phrase = pass_phrase[:i] + pass_phrase[i].upper() + pass_phrase[i+1:]
pass_phrase
'aPologizemodishly'

Insert random special and random digit into the passphrase.

i = secrets.randbelow(len(pass_phrase))+1
mangled_pass_phrase = pass_phrase[:i] + secrets.choice(special+digits) + pass_phrase[i:]
i = secrets.randbelow(len(mangled_pass_phrase))+1
mangled_pass_phrase = mangled_pass_phrase[:i] + secrets.choice(special+digits) + mangled_pass_phrase[i:]
mangled_pass_phrase
'aPo>lo%gizemodishly'
print('length of mangled passphrase {:d}'.format(len(mangled_pass_phrase)))
length of mangled passphrase 19

Every time the code is run, a new selection of words is made. On a previous run, the randomly chosen words were:
1: 16611 = avowedly
2: 5383 = hesitance
avowedlyhesitance
One generated example: ‘a@vowedlyhesitAn0ce’
length of mangled passphrase 19

This passphrase of 19 characters, is two random words and has a random capital letter, symbol and number inserted into the string of letters at random positions. The particular pass phrase “a@vowedlyhesitAn0ce” is but one instance in a vast pool of possible passphrases built with the algorithm. The hacker would need to try a vast number of three word combinations with a random capital letter, symbol and number inserted into the string of letters at random positions.

Implementation and use of the password card

My recommendation is not to use the password card for financial, email accounts or highly used social media accounts. These accounts, which are high value, and which I’ll call Tier 1 accounts, should have 16 to 20 character random character strings used as passwords. Tier 1 passwords should be written in a password book and kept secure. Furthermore, a dedicated computer should be used for financial Tier 1 accounts, not one that has been used for general web surfing and might have become infected from daily contact with the internet.

Generating the password card requires a computer and the ability to run the Python code shown in this notebook. The computer has most likely been connected to the internet at some time or is currently connected to the internet. This may not be acceptable if you have a high level of paranoia. Most people would be OK with generating, printing the card and then deleting or encrypting the backing up the files. A password card can be printed by copying the grid of characters to a word document for formatting and printing. Highlight alternate columns and font to Courier New size 12 for printing and lamination. Making an encrypted copy of the password card word document would probably be OK for most people. The printer and home computer are behind a locked door and the generation and use of the password card can be kept relatively private. After the card is printed, it could be laminated to make it more durable. You can find instructions for laminating cards online by using a household iron.

The secret phrase used with the password card should be written down somewhere until it becomes committed to memory. Additionally, 2FA should be enabled wherever it is available.

Security analysis

The purpose of this section is to perform an analysis of the password card and look at the security properties. A hacker will try to exploit exploit weaknesses found in the design or usage of the password card. Two common analysis techniques are black-box and white-box analysis.

A black-box analysis is an attack that works independently of the password algorithm. The most common black-box analysis is the brute-force search, where all known combinations are tried. A modified version is to try weak or short passwords, rather than an exhaustive search.

The opposite of a black-box analysis is a system where the inner components or logic are available for inspection. This is commonly referred to as a white-box or glass-box. This looks at the analysis from the point of view that the attacker knows how the algorithm works.

The security analysis will look at both points of view, with zero knowledge and complete knowledge.

Analysis of password card random characters

As described above, the secrets library can be used to generate the password’s random characters. The selection of letters, numbers and special characters is cryptographically random. Variations in the character set usually involve choosing the special characters. In this notebook, the following character sets are used.

character set
lower abcdefghijklmnopqrstuvwxyz
upper ABCDEFGHIJKLMNOPQRSTUVWXYZ
digits 0123456789
special !@#$%^&*()-+=<>?

The random part of the password card contains a random selection of: 2 upper + 2 lower + 2 digits + 2 special + 4 random, the number of combinations is:

# password card combinations
pwc_combinations = len(lower)**2 * len(upper)**2 * len(digits)**2 * len(special)**2 * char_set_size**4
num_of_chars = 12
print('possible combinations of {:d} characters in the password card is {:,.3e}'.format(num_of_chars, pwc_combinations))
possible combinations of 12 characters in the password card is 4.330e+17

Choosing the 12 random characters in the way that is used to generate the password card limits the number of possible combinations.

An adversary could attack the first six characters of the password since the first six characters only contain one upper case, one lower case, one special and two random characters. This was done to satisfy restrictive password policies. The second six characters are constructed similarly. This means that the password card will have at most three uppercase characters in the first group. Similarly, the same goes for lower case, digits and specials. This limitation can be exploited and reduce the search space. The code generating the password card will include at least one character from each group, but as a consequence, strings of five digits are excluded. This means that not all possible combinations are included in the password.

The calculation below shows that the number of combinations of 12 characters, if all combinations are allowed, is about five orders of magnitude larger than available on the password card.

num_of_chars = 12
print('combinations of {:d} characters from a character set of {:d} is {:,.3e}'.format(num_of_chars,
    char_set_size,char_set_size**num_of_chars))
combinations of 12 characters from a character set of 78 is 5.071e+22

Analysis of secret word space

The secret word, since it consists of two words concatenated, is called a passphrase. There are 32,768 five to nine character long words in the abridged word list. Most people will tend to pick shorter words or familiar words. Using dice will prevent this tendency from happening and being exploited.

wordlist_size = len(abridged_wordlist)
print('The number of combinations of two random words from the word list is {:,.3e}'.format(wordlist_size**num_words))
The number of combinations of two random words from the word list is 1.074e+09

Additionally the passphrase is mangled. The passphrase is two random words of minimum length of 5 characters, with word mangling, therefore not directly in any dictionary. The word mangling is obtained by randomly converting one character to uppercase, if there is not one already and inserting a random digit and special character into the passphrase. This will force the password cracker to exhaustively search the entire space of possibilities, since no substitution rules were followed.

To calculate the size of the passphrase space, we need to consider how many items there are. - There are two random words taken from a list of \(2^{15}\) words - There are two random characters inserted from a list of 26 digits and special characters - One character is converted to an uppercase if there is not already a upper case

print('number of items: {:,d}'.format((wordlist_size)**num_words + len(special) + len(digits)))
number of items: 1,073,741,850

There are two words and two special or digits selected from the number of items.

secretword_space = (len(abridged_wordlist)**num_words + len(special) + len(digits))**(num_words+1+1)
print('secret word space: {:.3e}'.format(secretword_space))
secret word space: 1.329e+36

Can also look at secret word space from the point of view as if it were 12 to 20 random characters.

print('Min secret word space, if considered as random chars: {:.3e}'.format(char_set_size**12))
print('Max secret word space, if considered as random chars: {:.3e}'.format(char_set_size**20))
Min secret word space, if considered as random chars: 5.071e+22
Max secret word space, if considered as random chars: 6.949e+37

Seems like calculating secret word space based on the way the secret word is built should generate a space between the limits.

Analysis of password card PIN

The PIN digits are not protected by any additional secrets. Someone who has access to your password card can read your PIN’s directly. For example, if you lose your wallet containing your ATM card and your password card, someone can use the card to access your funds. To defend against this, you could offset the PIN numbers used either by physical position or by adding modulo 10 some secret offset. In other words, actual PIN’s are shifted left or right by some position or the numbers offset in value by some amount; or maybe both.

Threat model

In this section attacks against passwords will be examined. A password or passphrase is typically a string of characters or a list or words, that can be random and is a secret shared between the individual and the computer system that confirms their identity.

Threat modeling is a risk analysis exercise where potential threats and mitigations are identified. In this notebook I’ll be looking at threats directly made on the password itself.

Attacks against passwords

Phishing, Man-in-the-Middle and Key-Logging attacks attempt to steal the password as does the so-called “$5 wrench attack”. Because these attacks do not exploit a weakness in the password itself, they are not covered in this analysis. Mitigations against password cracking threats will be discussed.

Credential Stuffing: Credential based attacks occur when attackers use previously cracked passwords at many different sites, looking for passwords that have been re-used.

Dictionary attacks: Password lists, Dictionary, word mangling: These attacks occur when attackers use dictionary lists of known cracked passwords, dictionary words and word mangling to attempt to guess passwords.

Brute force: This attack involves trying all combinations of characters up to some limit, looking for short passwords.

Examples of bad and compromised passwords

Examples of bad and compromised passwords are: Youknow123, drowssap, My2password, Qwerty12345@, StephenASmith1, Andrew24, ZaqXsw12, Johnny#12345, P@55w0rd, Jp1234567890, abdcefg, password, monkey, 123456, password, qwerty, football, baseball, welcome, abc123, 1qaz2wsx, dragon, master, monkey, letmein, login, princess, qwertyuiop, solo, passw0rd, starwars

Most systems enforce some level of password complexity, for example: Passwords need characters from all three of the following categories: - Uppercase characters - Lowercase characters - Non-alphanumeric characters

Most people use similar patterns (i.e. capital letter in the first position, a symbol in the last, and a number in the last 2). Password crackers know this, so they run their dictionary attacks using the common substitutions, such as “$” for “s”, “@” for “a,” “1” for “l” and so on. See the following article for password audit test.

Counter measures

The counter measures that can be used to strengthen passwords are those that limit attacks to brute force attacks. Using unique passwords for each login prevents compromised passwords from being used against other sites. Using passwords that are long and complex should be used. Dictionary words, combinations of words or names should not be used. Typical word mangling, letter substitutions pre and post pending of special characters or numbers are well known techniques for obfuscating dictionary words or names and should not be used.

Other mitigation

Multi-factor authentication (MFA) is an additional verification step used to gain access to an online account. Typically, secret questions, codes sent to your phone or hardware tokens are used as the second factor. MFA increases security because if one credential becomes compromised, unauthorized users will be unable to meet the second authentication requirement and will not be able to access the targeted physical space, computing device, network, or database. MFA should be enabled whenever it is supported. Web pages typically ask for MFA when an attempted login is from an unrecognized device or browser.

Password entropy

Austrian physicist Ludwig Boltzmann explained entropy as the measure of the number of possible microscopic arrangements or states of individual atoms and molecules of a system that comply with the macroscopic condition of the system. Password entropy is a measurement of how unpredictable or how hard it would be to try to guess the password. The number represents how many guesses one would have to make to try all the possible combinations. Depending on the system for which the password is being set up, the length of the password and the type characters allowed may be limited or the selection of certain characters enforced.

The strength of a password depends on the password entropy and the speed of the password security algorithm. Websites that are running old versions of security software protect users passwords with password hash algorithms that run very fast. If such a password file was hacked, the password cracking tools could try password guesses at a high rate and would have a higher chance of finding the password and spend less time doing so. Websites that are running up to date security software have implemented modern password hashing algorithms that run very slow on purpose. In this case the hacker, when trying a brute force attack could only cycle through his guesses at a slow rate. Documents that are password protected are typically protected with encryption that runs very fast. In this case password cracking tools can try passwords at a very high rate.

Password entropy is defined by the following formula:

\(E = log_2(R^L)\)

where:
E = number of bits of entropy
R = size of the pool of unique characters or elements or choices
L = number of items taken from the pool to form the password

The number E can be used to compare the strength of various password schemes, provided that R is determined correctly. For example the password

i6[y>Z#G#Y%q%Qh

which is 15 characters in length and has an entropy of 94.8 bits, since some ambiguous characters have been excluded. The password “\(\text{alongside gloss gigahertz yelp traffic}\)”, which is made from five random words chosen using Diceware with the word list from EFF’s New Wordlists for Random Passphrases has an entropy of 65 bits because R is the size of the word list and not the number of characters in the password. Eight Diceware words are needed to have enough entropy to exceed the 15 character password entropy. See calculations below.

pw_len = 15
char_set_len = len('abcdefghijkmnopqrstuvwxyz'+'ABCDEFGHJKLMNPQRSTUVWXYZ'+'23456789'+'!@#$%^&*()_+-={}[];:<>?')
print('password entropy {:.1f} bits'.format(math.log2(char_set_len**pw_len)))
password entropy 94.8 bits
num_words = 5 # five words from the diceware list
print('passphrase entropy {:d} bits'.format(int(math.log2((6**5)**num_words))))
passphrase entropy 64 bits
num_words = 8
print('passphrase entropy {:d} bits'.format(int(math.log2((6**5)**num_words))))
passphrase entropy 103 bits

The strength of a password is dependent on E, the password entropy and the query speed of the password hashing algorithm. Of course the query speed is also dependent on the computer hardware used to crack the passwords, which can range from a single modern desktop computer to large clusters of powerful processors.

The following are some sample calculations based on two key derivation functions with a sliding computational cost. In the first case, PBKDF2 is used with a low cost function. I’ll assign a speed of 10 nsec per query. In the second case Argon2 is used with a speed of 0.1 second per query. The speeds assigned are approximate and based on typical hardware.

As can be seen in the table below, a reasonable level of security, defined as searching 1% of the entropy pool in 100 years, can be obtained with a 64.8 bit key for PBKDF2 systems and 41.5 bits for Argon2 systems.

Time to search 1% of entropy pool strength #bits for 10ns/query #bits for 0.1s/query
1 week very weak 52.4 29.2
6 months weak 57.1 33.9
100 years reasonable 64.8 41.5
1k years strong 68.1 44.8
1M years very strong 78.1 54.8

The calculations are shown below.

sec_per_query = 10e-9
print('number of passwords evaluated per sec = {:.1e}'.format(1/sec_per_query))
pw_eval_per_hour = 1/sec_per_query*60*60 #*1e3
print('number of passwords evaluated per hour = {:.1f}B'.format(pw_eval_per_hour/1e9))
number of passwords evaluated per sec = 1.0e+08
number of passwords evaluated per hour = 360.0B
duration = {'1 week':7,'6 months':30*6,'100 years':365*100,'1000 years':365*1e3,'1M years':365*1e6}
N = Symbol('N')
print('Time to search 1% of the pool with {:,.1f}B passwords evaluated per hour'.format(pw_eval_per_hour/1e9))
for i in range(len(duration)):
    days = duration[list(duration)[i]]
    print('for E={:.1f} bits, {:s}'.format(solve(0.01*((2**N)/pw_eval_per_hour)/24-days, N)[0],list(duration)[i]))
Time to search 1% of the pool with 360.0B passwords evaluated per hour
for E=52.4 bits, 1 week
for E=57.1 bits, 6 months
for E=64.8 bits, 100 years
for E=68.1 bits, 1000 years
for E=78.1 bits, 1M years

Now looking at 0.1 sec per query.

sec_per_query = 0.1
print('number of passwords evaluated per sec = {:.1f}'.format(1/sec_per_query))
pw_eval_per_hour = 1/sec_per_query*60*60
print('number of passwords evaluated per hour = {:.1f}k'.format(pw_eval_per_hour/1e3))
number of passwords evaluated per sec = 10.0
number of passwords evaluated per hour = 36.0k
print('Time to search 1% of the pool with {:,.1f} passwords evaluated per hour'.format(pw_eval_per_hour))
for i in range(len(duration)):
    days = duration[list(duration)[i]]
    print('for E={:.1f} bits, {:s}'.format(solve(0.01*((2**N)/pw_eval_per_hour)/24-days, N)[0],list(duration)[i]))
Time to search 1% of the pool with 36,000.0 passwords evaluated per hour
for E=29.2 bits, 1 week
for E=33.9 bits, 6 months
for E=41.5 bits, 100 years
for E=44.8 bits, 1000 years
for E=54.8 bits, 1M years

The calculation above involves one hypothetical machine used to crack passwords. A hacker could use multiple machines working in parallel. The calculations below show how much password entropy would need to be increased to withstand a super cluster of 1000 machines or 1M machines.

num_machines = 1e3
print('to withstand a cluster of {:,.0f} machines, increase password entropy by {:.2f} bits'.format(num_machines, math.log2(num_machines)))
to withstand a cluster of 1,000 machines, increase password entropy by 9.97 bits
num_machines = 1e6
print('to withstand a cluster of {:,.0f} machine, increase password entropy by {:.2f} bits'.format(num_machines, math.log2(num_machines)))
to withstand a cluster of 1,000,000 machine, increase password entropy by 19.93 bits

The length of a password and equivalent bits of entropy is calculated below.

for i in range(8,21,2):
    print('password length of {:2d} characters equates to {:.1f} bits of entropy'.format(i,math.log2(char_set_size**i)))
password length of  8 characters equates to 50.3 bits of entropy
password length of 10 characters equates to 62.9 bits of entropy
password length of 12 characters equates to 75.4 bits of entropy
password length of 14 characters equates to 88.0 bits of entropy
password length of 16 characters equates to 100.6 bits of entropy
password length of 18 characters equates to 113.1 bits of entropy
password length of 20 characters equates to 125.7 bits of entropy

From the discussion above, a very strong password, that can resist attacks from a large cluster of fast password cracking machines, should be greater than 100 bits of entropy. To get 100 bits of entropy, the password needs to be 16 characters long or 8 words in a Diceware passphrase.

Hypothetical password cracking machine

Dictionary attacks are countered because the passwords are random. This leaves a brute force or an exhaustive search as the avenue of attack with a password cracking machine. A hypothetical password cracking computer is considered. This hypothetical computer or cluster of processors is somewhat similar to those used to mine crypto currencies, machines found in large server farms, botnets, AI learning machines or the type of machines available to government agencies. In this analysis, we assume that the password cracking computer is made of a cluster of 1,000 processors, working in cooperation and each can test 20T passwords per second.

# machine speed
num_computers = 1000 # number of computers in password cracking cluster
search_rate_per_computer = 20e12  # number of search operations per second
search_rate = search_rate_per_computer*num_computers
print('machine search rate = {:.1e} passwords per second'.format(search_rate))

pw_eval_per_hour = search_rate*60*60
print('password evaluated per hour = {:,.0f}T'.format(search_rate/1e12))
machine search rate = 2.0e+16 passwords per second
password evaluated per hour = 20,000T

The calculations below solve for the length of a password composed of random characters needed to resist the hypothetical password cracking machine for the duration indicated. Not all password cracking attempts are carried out by criminals. Sometimes users forget the password to a document or a bitcoin wallet and need to recover the password.

The following calculations show the amount of time required to search 1% of the possible passwords based on password length, assuming the password was composed of random letters, digits and special characters.

L = Symbol('L')
E = 80
print('{:,.1f} days to evaluate 1% of combinations from {:d} bits of entropy'.format(0.01*((2**E)/pw_eval_per_hour)/24,E ))
print('which is equivalent to a password of {:,.1f} random characters'.format(solve((char_set_size**L)-2**E, L)[0].evalf()))
7.0 days to evaluate 1% of combinations from 80 bits of entropy
which is equivalent to a password of 12.7 random characters
E = 85
print('{:,.1f} months to evaluate 1% of combinations from {:d} bits of entropy'.format(0.01*((2**E)/pw_eval_per_hour)/24/30,E ))
print('which is equivalent to a password of {:,.1f} random characters'.format(solve((char_set_size**L)-2**E, L)[0].evalf()))
7.5 months to evaluate 1% of combinations from 85 bits of entropy
which is equivalent to a password of 13.5 random characters
E = 90
print('{:,.1f} years to evaluate 1% of combinations from {:d} bits of entropy'.format(0.01*((2**E)/pw_eval_per_hour)/24/365,E ))
print('which is equivalent to a password of {:,.1f} random characters'.format(solve((char_set_size**L)-2**E, L)[0].evalf()))
19.6 years to evaluate 1% of combinations from 90 bits of entropy
which is equivalent to a password of 14.3 random characters
E = 100
print('{:,.1f} years to evaluate 1% of combinations from {:d} bits of entropy'.format(0.01*((2**E)/pw_eval_per_hour)/24/365,E ))
print('which is equivalent to a password of {:,.1f} random characters'.format(solve((char_set_size**L)-2**E, L)[0].evalf()))
20,098.5 years to evaluate 1% of combinations from 100 bits of entropy
which is equivalent to a password of 15.9 random characters

The calculations above show that a password of 16 random characters is needed to provide strong protection against the hypothetical password cracking machine.

Use of mnemonic and acronym passwords

There is a widely recommended formula that claims to build strong passwords called the mnemonic strategy. This strategy is often presented in articles on computer security when making recommendations for having strong passwords. This strategy starts with a memorable sentence or phrase, the words are abbreviated and combined to form a password with substitutions to include special characters and numbers. Two interesting papers on this topic can be found here [1] and here [2].

Using the mnemonic technique, passwords can be made by taking the first letter of each word or the last letter of each word. Other variations are possible such as omitting vowels or taking the first two letters from each word. Common substitutions or permutations are employed or numbers and special characters are appended. Some examples are shown below.

Phrase mnemonic source
Four score and seven years ago our fathers brought forth on this continent 4s&7yaofb4otc Gettysburg Address
Deep into that darkness peering Long I stood there wondering fearing, Doubting dreaming dreams di2tdpl1stwffddd Edgar Allen Poe
Because I could not stop for Death, He kindly stopped for me E1dtprheydte987 Emily Dickinson
Shall I compare thee to a summer’s day Shll1cmprthtsmmrsdy? William Shakespeare
Times have changed And we’ve often rewound the clock thcawortc1234 Cole Porter
Picture yourself in a boat on a river With tangerine trees and marmalade skies #pYiAbO@RwTtAmS* John Lennon, Paul Mccartney
All cats are gray llctsrgry pets
I like ice cream i_1ik3|c3Cr3@m$! food
It’s the size of the fight in the dog itsotfitd saying
In June 2013, my wife and I visited London, Paris and Amsterdam i63mw&ivLP&@ vacation
Radiate boundless love towards the entire world r1b2l3t&t*e+w Buddha
Conquer anger with non-anger Conquer badness with goodness C@wn@C6wg Buddha
Blessed are those who are persecuted because of righteousness for theirs is the kingdom of heaven d33o3d3f\(r\)$3mfn Matthew 5:10
But seek first his kingdom and his righteousness Bt5kfth5km@dh5r5 Matthew 6:33

The mnemonic passwords are long and complex and presumably easier to remember than 15 characters of gibberish.

The problem with “thcawortc1234” as a password, is that it is derived from a pool of song lyrics, maybe popular or obscure, it doesn’t matter; the pool of lyrics is not vast enough to be secure. A hacker could quickly run through all song lyrics, phrases from literature and poems, religious texts and build passwords to test with all variations of the mnemonic schemes described above. Notice how each of these is a fairly simple phrase and easy to remember. By stringing together a couple words, long passwords are created. Including a few symbols, numbers, or uppercase letters somewhere in the passphrase also increases its strength, but this is an illusion.

The problem with these seemingly clever schemes is that humans are not very random and the pool of passwords or pass phrases based on mnemonic or acronym passwords is not very large. Maybe to a human, it seems large, but a hacker can program a computer to test all variations in a short amount of time. Additionally, you should be using a unique password for each account, so you might have one hundred of these mnemonic passwords to remember. When combined with the password card, a mnemonic or acronym secret word would be OK and provide a sufficient level of security as long as the password card remained private. An online tool should not be used to generate the mnemonic.

Attacks against mnemonic passwords

In reference [2], a dictionary attack was made against mnemonic passwords. The authors used web scraping code to gather phrases from advertising slogans, children’s nursery rhymes and songs, movie quotes, famous quotations, song lyrics and television theme song lyrics. In total, they gathered over 249,000 phrases of which 129,000 phrases generated passwords with eight or more characters. Their mnemonic dictionary made from the mnemonic password rules and substitutions contained 400,000 items. The calculations below show that a pool of 400,000 items has an entropy of 18.6 bits per phrase, which is far less than the 100 bit recommendation.

References:
1. Yang, Weining & Li, Ninghui & Chowdhury, Omar & Xiong, Aiping & Proctor, Robert. (2016). An Empirical Study of Mnemonic Sentence-based Password Generation Strategies. 1216-1229. 10.1145/2976749.2978346. 2. Cynthia, Kuo & Romanosky, Sasha & Cranor, Lorrie. (2006). Human selection of mnemonic phrase-based passwords. ACM International Conference Proceeding Series. 149. 67-78. 10.1145/1143120.1143129.

print('{:,d} possible mnemonic passwords have an entropy of {:.1f} bits'.format(400000,math.log2(400000)))
400,000 possible mnemonic passwords have an entropy of 18.6 bits

If we are generous and allow that maybe the size of the pool of mnemonic passwords is 1 million, then:

print('{:,d} possible mnemonic passwords have an entropy of {:.1f} bits'.format(1000000,math.log2(1e6)))
1,000,000 possible mnemonic passwords have an entropy of 19.9 bits
print('the size of the pool needs to be on the order of {:.1e} items'.format(2**100))
the size of the pool needs to be on the order of 1.3e+30 items

How big is \(10^{30}\)? It is a number that is a million times greater than all the stars in all the galaxies in the universe. It is estimated that there are 10 trillion galaxies in the universe and 100 billion stars per galaxy. See: How many stars are in the universe?

galaxies = 10e12
stars_per_galaxy = 100e9
print('{:,.0f}'.format(1e30/galaxies/stars_per_galaxy))
1,000,000

Also, \(10^{30}\) is about the number of atoms as there are in 1,000 people.

False entropy estimates

Determining the entropy pool size can sometimes be difficult, especially if a human invented choosing method is used to generate passwords that people can remember. Password entropy estimates often just look at the number of characters in the password and not the size of the pool. This can sometimes give an entropy estimate that is much larger than it actually is. A correct entropy computation works over the process by which the password was generated. We should assume that the attacker is aware of the process, but lacks knowledge of the actual choices since they are random.

Referring to [1] and [2], I get the sense that human generated passwords have an entropy of 2 bits per character. The reason is that passwords chosen by humans roughly reflect the patterns and character frequency distributions of ordinary English text, and are chosen by users so that they can remember them. Users who choose passwords for themselves, because they are not chosen at random, the passwords will not have a uniform random distribution.

So as a rough estimate, a human chosen 15 character password will have 30 bits of entropy and as calculated below is equivalent to a random password of 4.8 characters.

References: 1. William E. Burr, Donna F. Dodson, Elaine M. Newton, Ray A. Perlner, W. Timothy Polk, Sarbari Gupta, Emad A. Nabbus,Electronic Authentication Guideline, NIST Special Publication 800-63-1, April 2006 2. Kristen K. Greene, John Kelsey, Joshua M. Franklin, Measuring the Usability and Security of Permuted Passwords on Mobile Platforms, NISTIR 8040, April 2016

L = Symbol('L')
E = 30
print('{:d} bits of entropy is equivalent to a random password of length {:,.1f} characters'.format(E,solve((char_set_size**L)-2**E, L)[0].evalf()))
30 bits of entropy is equivalent to a random password of length 4.8 characters

Attack scenarios

Risk analysis: Typically, risk analysis involves looking at the likelihood of an occurrence and then examining the severity of the consequences. We can adopt the position that all accounts should get a strong password regardless of the account or the consequences of that account being hacked. Nevertheless, email, heavily used social media and financial accounts belong in Tier 1 and should have very strong passwords and probably not candidates for the password card. Other accounts such as news sites and online shopping can have login passwords that do not need the same level of strength and can make use of the password card.

Attack avenues:
Password attack avenues described in news reports and discussed on line, involve exploiting weakness in the passwords chosen. Either the password chosen is predictable or too short. Password choices should come from the largest possible pool of choices and the selection should be random.

The following attack scenarios are examined. In each case the attacker possess varying amounts of knowledge. Alice is using the password card for several of her accounts. Faythe generated the password card for Alice as well as coming up with the secret passphrase that Alice appends to her password. Faythe can not be exploited by the hackers.

  1. Erin, who is Alice’s nosy sister, wants to read her email and “borrows” Alice’s password card.
  2. Chad, a hacker, tries to break into accounts by guessing usernames and passwords. He has no specific knowledge and uses smart guesses and social engineering to aid in his attack.
  3. Frank, a hacker, knows that Alice is using a password card, but he has no specific details.
  4. Trudy, an intruder, finds, steals or copies Alice’s password card. In this case, Trudy also knows Alice’s user name and the letters of the columns used in the password card. The only information Trudy does not possess is Alice’s secret word or pass phrase.
  5. Craig, a hacker, tricks Alice into loading a virus or visiting a malicious site and logs Alice’s password. He can employ various tools or techniques such as key logging, phishing, website spoofing or shoulder surfing.
  6. Mallory, a malicious and violent person, employs the so-called “$5 wrench attack”, which is when Mallory finds out that Alice has a lot of money and physically attacks or threatens her to obtain her passwords.

Scenario #1: Erin, the nosy sister of Alice, wants to read her email and “borrows” her password card

Erin does not know Alice’s secret word/passphrase, but thinks she is using two words plus extra characters, because she has watched Alice login, but didn’t quite get the full pass phrase.

Assuming that Alice’s email provided does not lock Alice’s account after three failed attempts, Erin could only try new password guesses at a rate of 1 to 3 seconds per attempt.

query_time = 1 # accounts for password latency for wrong password, on some systems this is 3 seconds
print('time to test 1% of all two word combinations = {:,.2e} years'.format(0.01*query_time*secretword_space/60/60/24/365))
time to test 1% of all two word combinations = 4.21e+26 years

Erin will give up long before she even gets to trying 1% of the possible combinations. Even if Erin gets lucky and tries Alice’s secret words, they will not work because of the word mangling.

Scenario #2: Chad, a hacker, tries to break into accounts by guessing usernames and passwords

Chad has no specific knowledge and uses smart guesses to guide his attack. Chad can only employ various password guessing strategies in hopes of finding a weak or short password. Since the password card generates a long complex password, the typical password cracking techniques will probably fail. In addition to guessing Alice’s password, Chad must also guess her login ID. Let’s say Chad has obtained the web site’s password hash table and tries to crack it off line with a massive machine.

Chad’s attack strategies: - dictionary, word mangle, lists of cracked passwords - exhaustive search through all combination of short passwords

Passwords generated with password card are not within the set of passwords that can be discovered by using attacks based on dictionary, word mangle or lists of cracked passwords. He can only search through all combinations of short passwords.

Chad has a password cracking computer similar to the hypothetical password cracking machine described above.

# machine speed
num_computers = 1000 # number of computers in password cracking cluster
search_rate_per_computer = 20e12  # number of search operations per second
search_rate = search_rate_per_computer*num_computers
print('machine search rate = {:.3e} passwords per second'.format(search_rate))
machine search rate = 2.000e+16 passwords per second

Chad can run his password cracking search through all the easy and short combinations. Eventually he tries to brute force the password and sets his search limit to 15 characters.

sec_per_hour = 60*60
pw_eval_per_hour = search_rate*sec_per_hour
print('number of passwords evaluated per hour = {:.1e}'.format(pw_eval_per_hour))
print('size of entropy pool: {:.1e}'.format((char_set_size**15)))
print('{:,.1f} years to evaluate 1% of all password card passwords'.format((0.01*(char_set_size**(15))/pw_eval_per_hour/24/365)))
number of passwords evaluated per hour = 7.2e+19
size of entropy pool: 2.4e+28
381.6 years to evaluate 1% of all password card passwords

It would take Chad more than 100 years to search 1% of the password space for 15 character length passwords. The chances of him getting lucky are very small. The password card plus the passphrase is typically longer than 15 characters.

print('number of passwords evaluated per hour = {:.1e}'.format(pw_eval_per_hour))
print('size of entropy pool: {:.1e}'.format((char_set_size**13)))
print('{:,.1f} days to evaluate 1% of all password card passwords'.format((0.01*(char_set_size**(13))/pw_eval_per_hour/24)))
number of passwords evaluated per hour = 7.2e+19
size of entropy pool: 4.0e+24
22.9 days to evaluate 1% of all password card passwords

It would take more than 20 days to search 1% of the space of a 13 character long password.

Scenario #3: Frank, a hacker, knows that Alice is using a password card

Frank knows that Alice is using a password card, but he does not have a copy of her card or the specific procedure that she is using. He can try to re-generate the card that Alice is using. Frank writes some Python code that can generate a new password card based on published descriptions of the card.

Any password card that Frank can generate will certainly be different than Alice’s because Faythe employed the Python secrets module when generating Alice’s card and choosing her secret word/passphrase. Frank doesn’t have any knowledge of the entropy pool that Faythe used and therefore cannot know how to initialize the internal states.

Frank might assume that instead of the secret.py module, Faythe used the random.py module. Frank could brute force attack the password card assuming that only \(2^{32}\) initial states are available. He would still need to attack the secret word/passphrase. Frank can only guess the length and how the secret word/passphrase might have been generated. Since Faythe generated the secret word/passphrase no amount of social engineering directed at Alice that Frank might try will work.

Frank can try all \(2^{32}\) initial states and all short secret word/passphrases that he can think of, but he will fail. It is improbable that Alice’s password card/passphrase would share any common states and at the same time guesses from a pool of short secret word/passphrase guesses. Faythe chose a longer passphrase than Frank is willing to test for.

print('number of passwords evaluated per hour = {:.1e}'.format(pw_eval_per_hour))
print('{:,.1f} years to evaluate 1% of 2^32 password card passwords'.format((0.01*((2**32)*(char_set_size**10))/pw_eval_per_hour/24/365)))
number of passwords evaluated per hour = 7.2e+19
567.6 years to evaluate 1% of 2^32 password card passwords

It would take Frank more than 500 years to search 1% of the space for \(2^{32}\) initial states and 10 character length secret word/passphrase. The password card plus the passphrase is typically longer than 15 characters and entropy used by secrets.py will greatly exceed \(2^{32}\).

Using the size of available combinations in the password card and the size of the secret word pool, the time to evaluate 1% of the total pool is extreme, as shown below.

print('{:,.3e} years to evaluate 1% of all password card passwords'.format((0.01*(pwc_combinations*secretword_space)/pw_eval_per_hour/24/365)))
9.126e+27 years to evaluate 1% of all password card passwords

Scenario #4: Trudy, an intruder, finds, steals or copies Alice’s password card

Trudy knows Alice’s user name and the details of her password card. The only information Trudy does not have is Alice’s secret word/passphrase. In this case, Alice’s password security is dependent on her secret word/passphrase. Trudy has a password cracking machine similar to Chad’s.

print('number of passwords evaluated per hour = {:.1e}'.format(pw_eval_per_hour))
print('{:,.3e} years to evaluate 1% of secret word space'.format(0.01*secretword_space/pw_eval_per_hour/24/365))
number of passwords evaluated per hour = 7.2e+19
2.107e+10 years to evaluate 1% of secret word space

The above calculations show that Trudy will not find Alice’s secret passphrase.

If Alice’s secret word resembles a 12 character random combination of characters. Then Trudy can search this space rather quickly, in less than 1 day.

print('{:,.3f} days to evaluate 1% of secret word space if equivalent to 12 characters'.format(0.01*char_set_size**12/pw_eval_per_hour/24))
0.293 days to evaluate 1% of secret word space if equivalent to 12 characters

To defend against Trudy, Alice’s mangled passphrase should be at least 15 characters long.

print('{:,.2f} years to evaluate 1% of secret word space'.format(0.01*char_set_size**15/pw_eval_per_hour/24/356))
391.22 years to evaluate 1% of secret word space

15 characters would force Trudy to spend enormous funds to search 1% of the space.

Scenario #5: Craig, a hacker, tricks Alice into loading a virus or visiting a malicious site and logs her password

Craig can employ various tools or techniques such as key logging, phishing, website spoofing or shoulder surfing to steal Alice’s password. The strength of the password does not help and it may be the reason Craig needs to resort to these measures.

Scenario #6: Mallory, a malicious attacker, employs the “5 dollar wrench attack”

Mallory finds out that Alice has a lot of money. He tricks her into visiting a malicious web page, opening a malicious email or physically breaks into her house and compromises her devices with malicious code to steal her passwords. He can also take the attack to the next level and physically attack or threaten her to obtain your passwords.

The strength of the password generated with the password card would probably leave scenarios 3 or 4 as the only practical avenue of attack. Which I guess is the point of having a strong password.

Analysis of alternatives

There are several password management schemes that are typically used. Assuming that unique passwords are being used for each login, there might be about 100 passwords that a typical user would need to keep track of. Three password management schemes are considered. - Password book: This is a handwritten password list in notebook, perhaps organized alphabetically in a binder. Use some sort of offline method and create good passwords. Keep the notebook in a secure place. Pros and cons of a computer based password manager verus a password book are discussed here. A password book can be purchased here. Backups of a password notebook can be made by photocopying (or scanning) the pages and keeping the backup in a safe deposit box. Other than banks and other financial institutions, most web sites will put a cookie on your computer and allow you to re-visit the site without re-logins. - Password card: A password card is also a paper solution, and one suitable to carry on a daily basis. A Password Card is a credit card-sized card which lets you pick very secure passwords, without having to remember them. Backups of the password card can be a photocopy which is kept in a secure location. - Computer based password manager: There are various options available and this is a big topic of discussion online. Some type of password manager is appropriate for most people and might require a subscription. Computer based password manager requires a very strong master password because it protects all your other passwords. - Combination of alternatives: A combination of a Password book, Password card and Computer based password manager can also be used.

Basically the options are paper based and computer based. Paper based solutions are only as secure as the storage location of the password book. A password book locked in your house at home and its existence is not known is pretty secure because no one is actively looking for such a book. Needless to say, you should not be carrying around your password book in your backpack with the words “passwords” on the front of it. A password book could be used to record all your passwords of which only a few are needed on a daily basis and the password card could be used for those needed more frequently or when away from home.

Web based passwords managers can be hacked. For example, Lastpass reported on December 22, 2022, that a threat actor was able to copy a backup of customer vault data from the encrypted storage container. The encrypted fields remain secured with 256-bit AES encryption and can only be decrypted with a unique encryption key derived from each user’s master password. Web based password managers are not recommended for financial or email accounts, mainly for two reasons. 1) They represent an attractive target with large payoff potential. 2) It breaks the don’t put all your eggs in one basket rule.

When you use a password manager like LastPass or 1Password, it stores a list containing all of the user names and passwords for the sites and apps you use, including banking, health care, email and social networking accounts. It keeps track of that list, called the vault, in its online cloud so you have easy access to your passwords from any device. LastPass said hackers had stolen copies of the list of usernames and passwords of every customer from the company’s servers.

Most web browsers offer at least a rudimentary password manager. This is better than reusing the same password everywhere, but browser-based password managers are limited. In recent years Google has improved the password manager built into Chrome.

Computer based password managers like Bitwarden offer a self hosted soultion in addition to the normal cloud based storage.

Cloud based password managers are targets for attack. See Bitwarden password vaults targeted in Google ads phishing attack which describes how Bitwarden and other password managers are being targeted in Google ads phishing campaigns to steal users’ password vault credentials. Most password managers are cloud-based, allowing users to access their passwords through websites and mobile apps. These passwords are stored in the cloud in “password vaults” that keep the data in an encrypted format, usually encrypted using users’ master passwords. Recent security breaches at LastPass and credential stuffing attacks at Norton have illustrated that a master password is a weak point for a password vault.

For this reason, threat actors have been spotted creating phishing pages that target your password vault’s login credentials, potentially authentication cookies, as once they gain access to these, they have full access to your vault. Typosquatting also called URL hijacking, a sting site, or a fake URL, is a form of cybersquatting, and possibly brandjacking which relies on mistakes such as typos made by Internet users when inputting a website address into a web browser. Should a user accidentally enter an incorrect website address, they may be led to any URL (including an alternative website owned by a cybersquatter).

The typosquatter’s URL will usually be one of five kinds, all similar to the victim site address: - A common misspelling, or foreign language spelling, of the intended site - A misspelling based on a typographical error - A plural of a singular domain name - A different top-level domain: (i.e. .com instead of .org) - An abuse of the Country Code Top-Level Domain (ccTLD) (.cm, .co, or .om instead of .com)

Once in the typosquatter’s site, the user may also be tricked into thinking that they are in fact in the real site, through the use of copied or similar logos, website layouts, or content.

Spam emails sometimes make use of typosquatting URLs to trick users into visiting malicious sites that look like a given bank’s site, for instance.

The domain used in the ad was ‘appbitwarden.com’ and, when clicked, redirected users to the site ‘bitwardenlogin.com.’ The page at ‘bitwardenlogin.com’ was an exact replica of the legitimate Bitwarden Web Vault login page.

Users should always configure Multi-factor authentication in the password manager. In case credentials are inadvertently entered into a phishing site, your multi-factor authentication should prevent a total breach. Unfortunately, even with MFA protection, your accounts can still be vulnerable to advanced adversary-in-the-middle (AiTM) phishing attacks. AiTM phishing attacks are when threat actors utilize specialized toolkits like Evilginx2, Modlishka, and Muraena to create phishing landing pages that proxy to legitimate login forms at a targeted service. Using this method, visitors to the phishing page will see a legitimate service’s login form, such as Microsoft 365. When they enter their credentials and MFA verification codes, this information is also relayed to the actual site. Once a user logs in and the legitimate site sends the MFA-backed session cookie, the phishing toolkit can steal these tokens for later use.

KeepassX is a password manager that is offline.

A master password is required for a password manager. The master password for a password manager is similar to having just one password, since with the master password all other passwords in the vault can be obtained. The Password Manager will encrypt the user’s password vault with something like AES-256 using a key derived from the master password. But if the master password is weak, then the derived 256 bit key does not provide a AES-256 level of protection.

A cloud based password manager requires a very strong master password, especially if all your sensitive account credentials are protected by the password manager. A 256 bit AES key derived from a process that starts with 90 equivalent bits of entropy is not 256 bits strong, but is only 90 bits strong. For example a pass phrase of four random words taken from a word list of 255,000 words, provides an equivalent password entropy of 72 bits.

pass_phrase_len = 4
word_list_len = 255e3
print('number of passphrases: {:.3e} with {:d} words'.format(word_list_len**pass_phrase_len,pass_phrase_len))
print('equivalent entropy of {:.0f} bits from using {:d} words'.format(math.log2(word_list_len**pass_phrase_len),pass_phrase_len))
number of passphrases: 4.228e+21 with 4 words
equivalent entropy of 72 bits from using 4 words

Such a password could be cracked in short order.

E = round(math.log2(word_list_len**pass_phrase_len))
print('{:,.0f} min to evaluate 1% of combinations from {:d} bits of entropy'.format(0.01*((2**E)/pw_eval_per_hour*60),E ))
print('which is equivalent to a password of length {:,.1f} characters'.format(solve((char_set_size**L)-2**E, L)[0].evalf()))
39 min to evaluate 1% of combinations from 72 bits of entropy
which is equivalent to a password of length 11.5 characters

If the 256 bit encryption key is derived from a short pass phrase, the effective key length is 72 bits and not 256 bits.

If the master password word is 16 random characters of gibberish, the equivalent key length is 101 bits.

# how many characters in a jibberish password?
num_of_chars = 16
char_set_size = len('abcdefghijklmnopqrstuvwxyz'+'ABCDEFGHIJKLMNOPQRSTUVWXYZ'+'0123456789'+'!@#\$%^&*()-+=<>?')
E = round(math.log2(char_set_size**num_of_chars))
print('equivalent entropy of {:.0f} bits from {:d} random characters'.format(E,num_of_chars))
print('{:,.0f} years to evaluate 1% of combinations from {:d} bits of entropy'.format(0.01*((2**E)/pw_eval_per_hour)/24/365,E ))
equivalent entropy of 101 bits from 16 random characters
40,197 years to evaluate 1% of combinations from 101 bits of entropy

You need 21 random characters or 8 random words to exceed the entropy of a 128 bit key.

Conclusion

The password card is secure when coupled with a 15 character long mangled passphrase. The 15 character long mangled passphrase is the only part that needs to be memorized. Using the procedure outlined, a unique and strong password can be generated for each account. The password card provides a convenient and portable way to generate and use unique passwords.

Revision History

  • 10/10/2015: Ver 1 - coding started
  • 20 Nov 2022: Python program converted to this IPython notebook