Skip to content

Instantly share code, notes, and snippets.

@louisswarren
Last active April 30, 2022 10:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save louisswarren/92da1771fe8feee1f1708e0f4c303b3e to your computer and use it in GitHub Desktop.
Save louisswarren/92da1771fe8feee1f1708e0f4c303b3e to your computer and use it in GitHub Desktop.
Generate passwords which are easy to type
# Generate passwords which can be typed without using any finger to press two
# different keys in a row.
from math import log, ceil
# For each finger, write the letters *you* type with that finger.
finger_classes = [
'qaz',
'wsx',
'edc',
'rfvtgbc',
'yhnujmb',
'ik',
'olp',
'p',
]
def word_filter(x):
# Remove overly short words
if len(x) < 4: return False
# Remove words which aren't lowercase alphabetic
allowed_chars = list(chr(i) for i in range(ord('a'), ord('z') + 1))
if any(c not in allowed_chars for c in x): return False
# Remove duplicated key presses
for i in range(1, len(x)):
for cls in finger_classes:
if x[i] in cls:
if x[i-1] != x[i] and x[i-1] in cls:
return False
return True
def good_words(word_list):
order = lambda x: (-len(x), x)
return sorted(filter(word_filter, word_list), key=order)
def needed_words(num_words, required_bits):
return ceil(required_bits / log(num_words, 2))
def entropy(n):
return log(n, 2)
def genpwd(words, n = 4):
from random import randrange
return ' '.join(words[randrange(len(words))] for _ in range(n))
def main():
with open('/usr/share/dict/words') as f:
word_list = f.read().split()
words = good_words(word_list)
print("{} usable words found".format(len(words)))
print("Four word passphrases give {:0.2f} bits of entropy".format(
entropy(len(words) ** 4)))
print("Passphrases need {} words for {} bits of entropy".format(
needed_words(len(words), 64), 64))
print("You lose entropy by not accepting the first passphrase shown")
print("as you are restricting to phrases you like. Assuming you read")
print("down the list in order until you find one that you like, the")
print("entropy of your chosen passphrase is on the left.")
print()
n = 4
for i in range(1, 17):
pwd_entropy = entropy(len(words) ** n / i)
print("{:0.2f}\t{}".format(pwd_entropy, genpwd(words, n)))
if __name__ == '__main__':
main()
"""
35078 usable words found
Four word passphrases give 60.39 bits of entropy
Passphrases need 5 words for 64 bits of entropy
inconspicuous tawniest tasting obsessions
peristyles weigh mouthier weepie
cinch vigorous veterinaries pricy
suckers taskmaster rejigs anemometer
negligee hotshots joists disesteem
fuzzes styptic angelfish layabout
housewifely salves stuffings beltways
jobs ayah alliteration coverups
vampire super arose pinnacle
charabanc biracial reproachful nominee
lineups championship canes clauses
vigils ladyship internalises automobile
spirally drippy sawyers gouge
valeting soapsuds fleeter roomette
contouring islets odalisques tier
quarrellers proton tonsils mayors
"""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment