[Freeipa-devel] [freeipa PR#317][comment] Unify password generation across FreeIPA
pspacek
freeipa-github-notification at redhat.com
Mon Dec 12 14:08:51 UTC 2016
URL: https://github.com/freeipa/freeipa/pull/317
Title: #317: Unify password generation across FreeIPA
pspacek commented:
"""
Talk is cheap so here is the code!
~~~
import math
import string
import random
class TokenGenerator(object):
"""Tunable token generator."""
# without: = # ' " \ `
_special = '!$%&()*+,-./:;<>?@[]^_{|}~'
def_charsets = {
'uppercase':
{'chars': string.ascii_uppercase,
'entropy': math.log(len(string.ascii_uppercase), 2)},
'lowercase':
{'chars': string.ascii_lowercase,
'entropy': math.log(len(string.ascii_lowercase), 2)},
'digits':
{'chars': string.digits,
'entropy': math.log(len(string.digits), 2)},
'special':
{'chars': _special,
'entropy': math.log(len(_special), 2)},
}
def __init__(self, uppercase=0, lowercase=0, digits=0, special=0,
min_len=0):
"""Specify character contraints on generated tokens.
Integer values specify minimal number of characters from given
character class and length.
Value False prevents given character from appearing in the token.
Example:
TokenGenerator(uppercase=3, lowercase=3, digits=0, special=False)
At least 3 upper and 3 lower case ASCII chars, may contain digits,
no special chars.
"""
self.rng = random.SystemRandom()
self.min_len = min_len
self.req_classes = dict(
uppercase=uppercase,
lowercase=lowercase,
digits=digits,
special=special
)
self.todo_charsets = self.def_charsets.copy()
# 'all' class is used when adding entropy to too-short tokens
# it contains characters from all allowed classes
self.todo_charsets['all'] = {'chars': ''.join(
[charclass['chars']
for charclass_name, charclass
in self.todo_charsets.items()
if self.req_classes[charclass_name] is not False]
)}
self.todo_charsets['all']['entropy'] = math.log(
len(self.todo_charsets['all']['chars']), 2)
def __call__(self, req_entropy=128):
"""Generate token containing at least req_entropy bits.
req_entropy is minimal number of entropy bits attacker has to guess:
128 bits entropy: secure
256 bits of entropy: secure enough if you care about quantum computers
The generated token will fulfill containts specified in init.
"""
todo_entropy = req_entropy
password = ''
# Generate required character classes:
# The order of generated characters is fixed to comply with check in
# NSS function sftk_newPinCheck() in nss/lib/softoken/fipstokn.c.
for charclass_name in ['digits', 'uppercase', 'lowercase', 'special']:
charclass = self.todo_charsets[charclass_name]
todo_characters = self.req_classes[charclass_name]
while todo_characters > 0:
password += random.choice(charclass['chars'])
todo_entropy -= charclass['entropy']
todo_characters -= 1
# required character classes do not provide sufficient entropy
# or does not fulfill minimal length constraint
allchars = self.todo_charsets['all']
while todo_entropy > 0 or len(password) < self.min_len:
password += random.choice(allchars['chars'])
todo_entropy -= allchars['entropy']
return password
if __name__ == '__main__':
pwgen = TokenGenerator()
for i in range(100):
print(pwgen(256))
~~~
This code deterministically generates passwords. If character constraints are specified, the code might generate slightly longer passwords than the brute-force method. For example, 256 bit password with FIPS-compliant constrains (3 character classes) the difference is 41 vs. 40 characters. Given this different, I think that determinism trumphs shorter passwords.
Also, I think that it does not make sense to have `req_entropy` parameter in `__call__`. IMHO it makes sense to specify it along with other constrains in `__init__`.
"""
See the full comment at https://github.com/freeipa/freeipa/pull/317#issuecomment-266439544
More information about the Freeipa-devel
mailing list