Site Map - skip to main content

Hacker Public Radio

Your ideas, projects, opinions - podcasted.

New episodes Monday through Friday.


hpr3107 :: Generating comfortable passwords

generating passwords to be comfortably type-able

<< First, < Previous, Latest >>

Hosted by crvs on 2020-06-30 is flagged as Explicit and is released under a CC-BY-SA license.
Tags: passwords, python.
Listen in ogg, spx, or mp3 format. | Comments (1)

Random Password Generation

First implementation: 14 character long with 6 letters and 8 digits

#!/usr/bin/env python3
# file: passgen-v1.py

import random

LETTERS = "abcdefghijklmnopqrstuvwxyz"

if __name__ == "__main__":

    passwd = []

    for i in range(6):
            passwd.append(random.choice(LETTERS))

    for i in range(8):
            passwd.append(random.choice("1234567890"))

    print("".join(passwd))

The passwords that come out of this are a bit difficult to type so I forced it to alternate between the left and right hands

#!/usr/bin/env python3
# file: passgen-v2.py

import random

LEFTS = "',.pyaoeui;qjkx"
RIGHTS = "fgcrldhdhtns-bmwvz"

if __name__ == "__main__":

    passwd = []

    for i in range(6):
        if i % 2 == 0:
            passwd.append(random.choice(LEFTS))
        else:
            passwd.append(random.choice(RIGHTS))

    for i in range(8):
        if i % 2 == 0:
            passwd.append(random.choice("123456"))
        else:
            passwd.append(random.choice("7890"))

    print("".join(passwd))

The regularity of switching between left and right hands (intuitively, and almost surely) decreases the entropy of the password, so use markov models to make that happen for the most part but critically NOT enforce it.

#!/usr/bin/env python3
# file: passgen-v3.py

import random

Ls = [
    "aoeui",  # L1
    "',.py",  # L2
    ";qjkx",  # L3
    "123456", # L4
    "-snthd", # R1
    "lrcgf",  # R2
    "zvwmb",  # R3
    "7890"    # R4
]

A = [[ .03,  .03,  .03, .01,  .27,  .27,  .27, .09],
     [ .03,  .03,  .03, .01,  .27,  .27,  .27, .09],
     [ .03,  .03,  .03, .01,  .27,  .27,  .27, .09],
     [.004, .003, .003, .09,  .03,  .03,  .03, .81],
     [ .27,  .27,  .27, .09,  .03,  .03,  .03, .01],
     [ .27,  .27,  .27, .09,  .03,  .03,  .03, .01],
     [ .27,  .27,  .27, .09,  .03,  .03,  .03, .01],
     [ .03,  .03,  .03, .81, .004, .003, .003, .09]]

pi = [ .41, .03, .03, .03, .41, .03, .03, .03]

def sample( l ):
    l_partial = [ sum(l[:i+1],0) for i in range(len(l))]
    u = random.uniform(0,1)
    for j,v in enumerate(l_partial):
        if v > u:
            return j

if __name__ == "__main__":

    passwd = []

    s = sample(pi)
    for i in range(20):
        s = sample(A[s])
        passwd.append(random.choice(Ls[s]))

    print("".join(passwd))

For increased entropy should also consider peppering in a few upper case characters.

#!/usr/bin/env python3
# file: passgen-v3.py

import random

Ls = [
    "aoeui",  # L1
    "',.py",  # L2
    ";qjkx",  # L3
    "123456", # L4
    "-snthd", # R1
    "lrcgf",  # R2
    "zvwmb",  # R3
    "7890"    # R4
]

A = [[ .03,  .03,  .03, .01,  .27,  .27,  .27, .09],
     [ .03,  .03,  .03, .01,  .27,  .27,  .27, .09],
     [ .03,  .03,  .03, .01,  .27,  .27,  .27, .09],
     [.004, .003, .003, .09,  .03,  .03,  .03, .81],
     [ .27,  .27,  .27, .09,  .03,  .03,  .03, .01],
     [ .27,  .27,  .27, .09,  .03,  .03,  .03, .01],
     [ .27,  .27,  .27, .09,  .03,  .03,  .03, .01],
     [ .03,  .03,  .03, .81, .004, .003, .003, .09]]

pi = [ .41, .03, .03, .03, .41, .03, .03, .03]

UPPER=.1

def sample( l ):
    l_partial = [ sum(l[:i+1],0) for i in range(len(l))]
    u = random.uniform(0,1)
    for j,v in enumerate(l_partial):
        if v > u:
            return j

if __name__ == "__main__":

    passwd = []

    s = sample(pi)
    for i in range(20):
        s = sample(A[s])
        c = random.choice(Ls[s])
        u = random.uniform(0,1)
        if u < UPPER:
            c = c.upper()
        passwd.append(c)

    print("".join(passwd))

Finally, generating the matrix by hand was a bit of a pain, so I made it a bit easier by making a small procedure with few control knobs (the variables SWITCH_HAND and SWITCH_CHAR which control how likely it is that a character pair will result in a hand switch, or a switch between general characters and digits).

#!/usr/bin/env python3

import random
import numpy

# this version uses a markov chain to make it more likely to alternate hands
# (in dvorak) so that the password is easy to type (in dvorak)

Ls = [
    "aoeui",  # L1
    "',.py",  # L2
    ";qjkx",  # L3
    "123456", # L4
    "-snthd", # R1
    "lrcgf",  # R2
    "zvwmb",  # R3
    "7890"    # R4
]

SWITCH_HAND = .8
SWITCH_CHAR = .3
UPPER=.1

def prob( i , j ):
    switch_hand = int(i / 4) != int(j / 4)
    to_num = (j % 4) == 3
    from_num = (i % 4) == 3

    prob = 1

    if to_num and from_num:
        prob *= (1 - SWITCH_CHAR)
    elif to_num:
        prob *= (SWITCH_CHAR)
    elif from_num:
        prob *= (SWITCH_CHAR / 3)
    else:
        prob *= ((1 - SWITCH_CHAR) / 3)

    if switch_hand:
        prob *= SWITCH_HAND
    else:
        prob *= (1 - SWITCH_HAND)

    return prob


A = numpy.array([ [ prob(i,j) for j in range(8)  ] for i in range(8) ])

pi = [ 1.0 / 8 for i in range(8) ]

def sample( l ):
    l_partial = [ sum(l[:i+1],0) for i in range(len(l))]
    u = random.uniform(0,1)
    for j,v in enumerate(l_partial):
        if v > u:
            return j

if __name__ == "__main__":

    passwd = []

    s = sample(pi)
    for i in range(20):
        s = sample(A[s])
        c = random.choice(Ls[s])
        u = random.uniform(0,1)
        if u < UPPER:
            c = c.upper()
        passwd.append(c)

    print("".join(passwd))

Comments

Subscribe to the comments RSS feed.

Comment #1 posted on 2020-06-30T21:47:20Z by sigflup

Thanks

Thanks for this one!

<< First, < Previous, Latest >>

Leave Comment

Note to Verbose Commenters
If you can't fit everything you want to say in the comment below then you really should record a response show instead.

Note to Spammers
All comments are moderated. All links are checked by humans. We strip out all html. Feel free to record a show about yourself, or your industry, or any other topic we may find interesting. We also check shows for spam :).

Provide feedback
Your Name/Handle:
Title:
Comment:
Anti Spam Question: What does the P in HPR stand for ?