You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

71 lines
2.7 KiB

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import argparse, math, random
from pyutil.mathutil import div_ceil
from pkg_resources import resource_stream
def recursive_subset_sum(entropy_needed, wordlists):
# Pick a minimalish set of numbers which sum to at least
# entropy_needed.
# Okay now what's the smallest number of words which will give us
# at least this much entropy?
entropy_of_biggest_wordlist = wordlists[-1][0]
assert isinstance(entropy_of_biggest_wordlist, float), wordlists[-1]
needed_words = div_ceil(entropy_needed, entropy_of_biggest_wordlist)
# How much entropy do we need from each word?
needed_entropy_per_word = entropy_needed / needed_words
# What's the smallest wordlist that offers at least this much
# entropy per word?
for (wlentropy, wl) in wordlists:
if wlentropy >= needed_entropy_per_word:
break
assert wlentropy >= needed_entropy_per_word, (wlentropy, needed_entropy_per_word)
result = [(wlentropy, wl)]
# If we need more, recurse...
if wlentropy < entropy_needed:
rest = recursive_subset_sum(entropy_needed - wlentropy, wordlists)
result.extend(rest)
return result
def gen_passphrase(entropy, allwords):
maxlenwords = []
i = 2 # The smallest set is words of length 1 or 2.
words = [x for x in allwords if len(x) <= i]
maxlenwords.append((math.log(len(words), 2), words))
while len(maxlenwords[-1][1]) < len(allwords):
i += 1
words = [x for x in allwords if len(x) <= i]
maxlenwords.append((math.log(len(words), 2), words))
sr = random.SystemRandom()
passphrase = []
wordlists_to_use = recursive_subset_sum(entropy, maxlenwords)
passphraseentropy = 0.0
for (wle, wl) in wordlists_to_use:
passphrase.append(sr.choice(wl))
passphraseentropy += wle
return (u".".join(passphrase), passphraseentropy)
def main():
parser = argparse.ArgumentParser(prog="chbs", description="Create a random passphrase by picking a few random words.")
parser.add_argument('-d', '--dictionary', help="what file to read a list of words from (or omit this option to use chbs's bundled dictionary)", type=argparse.FileType('rU'), metavar="DICT")
parser.add_argument('bits', help="how many bits of entropy minimum", type=float, metavar="BITS")
args = parser.parse_args()
dicti = args.dictionary
if not dicti:
dicti = resource_stream('pyutil', 'data/wordlist.txt')
allwords = set([x.decode('utf-8').strip().lower() for x in dicti.readlines()])
passphrase, bits = gen_passphrase(args.bits, allwords)
print u"Your new password is: '%s'. It is worth about %s bits." % (passphrase, bits)