Skip to content

Instantly share code, notes, and snippets.

@yaunj
Last active September 19, 2019 10:45
Show Gist options
  • Save yaunj/ab420e8358350c4ed39a476bf97a79c3 to your computer and use it in GitHub Desktop.
Save yaunj/ab420e8358350c4ed39a476bf97a79c3 to your computer and use it in GitHub Desktop.
xkcd style password generator
#!/usr/bin/env python
"""Generate a password in similar vein to XKCD 936"""
from __future__ import print_function, unicode_literals
import random
import string
class XKCDPasswordGenerator:
"""Generate a password in similar vein to XKCD 936"""
def __init__(self, dictionary_file='/usr/share/dict/words'):
self.dictionary = get_dict(dictionary_file)
def get_dictword(self, length):
"""Return a random dictionary word of required length"""
return random.choice(list(word.strip() for word in self.dictionary if len(word) > length))
def genpass(self, words, digits):
"""Generate a random XKCD 936 password"""
wordcount, wordlength = words
padding = random.choice(string.punctuation) * 2
separator = random.choice(string.punctuation)
pwparts = [padding, get_digits(digits), separator]
for i in range(wordcount):
if i % 2 == 0:
case = str.lower
else:
case = str.upper
pwparts.append(case(self.get_dictword(wordlength)))
pwparts.append(separator)
pwparts.append(get_digits(digits))
pwparts.append(padding)
return ''.join(map(str, pwparts))
def get_digits(digits):
"""Return a random number with required number of digits"""
return random.randint(10 ** (digits - 1), (10 ** digits) - 1)
def get_dict(filename='/usr/share/dict/words'):
"""Get sanitized words from a dictionary file"""
words = []
with open(filename) as dictfile:
for word in dictfile:
word = word.strip()
if all(c in string.ascii_letters for c in word):
words.append(word)
return words
def main():
"""Main entry point"""
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-w', '--words', help='number of words', type=int, default=3)
parser.add_argument('-W', '--word-length', help='minimum word length', type=int, default=4)
parser.add_argument('-d', '--digits', help='number of digits', type=int, default=2)
parser.add_argument('-n', '--num-passwords', help='number of passwords to generate', type=int, default=1)
args = parser.parse_args()
generator = XKCDPasswordGenerator()
for _ in range(args.num_passwords):
print(generator.genpass((args.words, args.word_length), args.digits))
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment