Skip to content

Instantly share code, notes, and snippets.

@erinaceous
Last active December 30, 2015 10:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save erinaceous/7815007 to your computer and use it in GitHub Desktop.
Save erinaceous/7815007 to your computer and use it in GitHub Desktop.
Tiny guide for getting workable Aber passwords
#!/usr/bin/env python
"""
Password generator for Aber Uni users
Owain Jones [github.com/doomcat]
IS's systems are very picky about the passwords they let you use. It
can prove to be very difficult to come up with a password that's both
memorable and secure for it.
What I had started doing is generating passwords which are accepted
by IS, but hard to remember. However, they were easy to reproduce,
if you knew the original word it was based on and the method used to
create it -- essentially generating a hash of a string of memorable
text. Except not using anything common like MD5 or SHA hashing.
I've since extended that to create passwords like this that are also
fairly memorable after seeing them a few times, using the "rule of
threes" -- it's easier to remember passwords if you can chunk them up
into sets of three characters, as you can give them a rhythm then.
Same thing we do with phone numbers, postcodes and license plates.
So, what this script does:
- Takes a normal, short string.
- Creates a hash of it that is guaranteed to be a multiple of
three in length.
- Splits that hash up into sets of three
- Uppercases and lowercases the sets at random:
abc DEF GHI jkl
YMMV when it comes to remembering groups of three things, maybe it's
just me it works for. Maybe you're weird and you remember things in
groups of four best. So I've made that changeable.
"""
from __future__ import print_function
import hashlib
SET_LENGTH = 3 # default length of the character sets
NUM_CHUNKS = 4
UPPERCASED = [0, 1, 0, 1]
SEPARATOR = ' '
def hashing_method(string):
"""However you want to hash things.
Arguments:
string: Plaintext string
Returns: Hash of original string.
"""
return hashlib.sha1(str(string).encode("utf-8")).hexdigest().lower()
def do_the_thing(password, set_length=SET_LENGTH, chunks=NUM_CHUNKS,
uppercase_chunks=UPPERCASED, separator=SEPARATOR,
hashing_method=hashing_method):
"""Do everything mentioned in this module's docstring.
Arguments:
password: The plaintext password you want to create a better
password from.
set_length: The length of each chunk of the password. Defaults to
SET_LENGTH.
chunks: Number of chunks to use for your password.
Default: NUM_CHUNKS.
uppercase_chunks: Which of the chunks to make uppercase. Default:
UPPERCASED
separator: The separator to use between chunks. Default: SEPARATOR
hashing_method: The method used to generate a hash from the
original password. Defaults to this module's
"hashing_method()" function. Can be any function
that takes and returns a single string.
Returns: (Hopefully) a password that IS will actually accept :)
"""
pw_hashed = hashing_method(password)
chunks = [pw_hashed[x:(x + set_length)] for x in range(0, chunks)]
for i, x in enumerate(uppercase_chunks):
if x == 1:
chunks[i] = chunks[i].upper()
return separator.join(chunks)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument(
"password",
help="The (plaintext) password you want to base your password on")
parser.add_argument(
"-l", "--section-length", type=int, default=SET_LENGTH,
help="The length of each memorable chunk of password (Default: %d)" %
SET_LENGTH)
args = parser.parse_args()
print(do_the_thing(args.password))
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=windows-1252" http-equiv="content-type">
<title>Generating passwords for Aber Uni accounts</title>
</head>
<body>
<h1>Generating Passwords that Aber Uni's Password System will Actually
Accept</h1>
<p>So anyone who's had to fight the mandatory once-per-year password
changing system knows that it is the most fussy thing in the world. The
rules seem to be:</p>
<ul>
<li>No words (in any language)</li>
<li>No random letters</li>
<li>No ASCII characters</li>
<li>No glyphs that human beings could possibly comprehend</li>
</ul>
And it can be pretty difficult to create a password that is memorable yet
cryptic enough for the password system to be satisfied with.<br>
<br>
I've developed my own little system for generating passwords. Instead of
generating passwords I can <em>directly remember</em>, I generate passwords
which I can <strong>remember how I generated</strong>.<br>
For example (demonstrated with a bit of Python code):<br>
<ol>
<li>Take a plaintext passphrase, one you will easily remember. For the
purposes of this, I chose <code>butts</code>.</li>
<li>Generate a hash of the phrase. I chose to use an MD5 hash -- I'm only
using the first part of the hash<span style="font-family: monospace;">:<br>
<code>import hashlib<br>
pw_hashed = hashlib.sha1("butts".encode("utf-8")).hexdigest()</code></span><br>
The hash I got was <code>cd89a20adde7a608f3331e71c37bdfa087bacbf3</code></li>
<li>Divide the hash into fixed-length chunks. I chose to use four chunks
of three characters, because I find things in sets of three easier to
remember.<br>
<code>chunks = [pw_hashed[x:x+3] for x in range(0, 4)]</code><br>
The resulting list: <code>['cd8', 'd89', '89a', '9a2']</code></li>
<li>Next, I make some of the chunks uppercase in a pattern I'm going to
remember:<br>
<code>pattern = [0, 1, 0, 1]<br>
for i, x in enumerate(pattern):<br>
&nbsp;&nbsp;&nbsp; if x == 1:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; chunks[i] =
chunks[i].upper()</code><br>
(So now <code>chunks</code> looks like this: <code>['cd8', 'D89',
'89a', '9A2']</code>)</li>
<li> Finally, I join the chunks back together into a string, with
something separating the chunks. In this example, I'm just using spaces,
you could use dots or dashes or whatever though:<br>
<code>print(' '.join(chunks))</code></li>
<li>Which gets me my password; <code>cd8 D89 89a 9A2</code></li>
</ol>
<p>This is just an example; the hashing or the dividing into chunks and
stuff isn't set in stone. The point is to remember how to <strong>programatically
recreate </strong>a password by remembering <strong>how you coded it</strong>
rather than trying to memorize a string of random characters straight
away. That way you don't have to have it lying around anywhere, and you
don't need to keep the plaintext original passphrase you chose written
down either because it'll be something you can remember super easy :) It
should be safe to keep scripts to (re)generate passwords lying around as
long as you don't store your original passphrase in it (so pass it in as
an argument to the code).</p>
<p>I turned the example Python code on this page into a working script. It's
<a href="actual_aber_password.py">part of this gist</a>. Feel free to
<a href="https://gist.github.com/doomcat/7815007">fork this</a>
and point out any flaws or ways to make this better
(more memorable password hashes, etc.)</p>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment