Skip to content

Instantly share code, notes, and snippets.

@eur0pa
Created August 1, 2015 19:58
Show Gist options
  • Save eur0pa/ec1685f0e75a294f1c06 to your computer and use it in GitHub Desktop.
Save eur0pa/ec1685f0e75a294f1c06 to your computer and use it in GitHub Desktop.
#!/usr/bin/python2
# -*- coding: utf-8 -*-
import sys
import re
import hashlib
import hmac
from base64 import b64encode
class PassHash(object):
def __init__(self, args):
super(PassHash, self).__init__()
self.site_tag = args.site_tag
self.master_key = args.master_key
self.hashword_size = args.length if args.length > 0 and args.length <= 27 else 27
self.require_digit = args.require_digit
self.require_punctuation = args.require_punctuation
self.require_mixedcase = args.require_mixedcase
self.restrict_special = args.restrict_special
self.restrict_digits = args.restrict_digits
self.password = self.generate_hashword()
def generate_hashword(self):
s = b64encode(hmac.new(self.master_key, self.site_tag, hashlib.sha1).digest())
if s[-1] == '=':
s = s[:-1]
sum = 0
for i in s:
sum += ord(i)
if self.restrict_digits:
s = self._convert_to_digits(s, sum)
else:
if self.require_digit:
s = self._inject_special_character(s, 0, 4, sum, 48, 10)
if self.require_punctuation and not self.restrict_special:
s = self._inject_special_character(s, 1, 4, sum, 33, 15)
if self.require_mixedcase:
s = self._inject_special_character(s, 2, 4, sum, 65, 26)
s = self._inject_special_character(s, 3, 4, sum, 97, 26)
if self.restrict_special:
s = self._remove_special_characters(s, sum)
return s[:self.hashword_size]
def _inject_special_character(self, sInput, offset, reserved, seed, cStart, cNum):
pos0 = seed % self.hashword_size
pos = (pos0 + offset) % self.hashword_size
for i in xrange(self.hashword_size - reserved):
i2 = (pos0 + reserved + i) % self.hashword_size
c = ord(sInput[i2])
if c >= cStart and c < cStart + cNum:
return sInput
sHead = sInput[:pos]
sInject = chr(((seed + ord(sInput[pos])) % cNum) + cStart)
sTail = sInput[pos+1:]
return sHead + sInject + sTail
def _convert_to_digits(self, sInput, seed):
s = ''
for c in sInput:
if not c.isdigit():
s += chr((seed + ord(c)) % 10 + 48)
else:
s += c
return s
def _remove_special_characters(self, sInput, seed):
s = ''
for c in sInput:
if not c.isalnum():
s += chr((seed + len(s)) % 26 + 65)
else:
s += c
return s
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(description='Python2 implementation of PassHash')
parser.add_argument(
'site_tag',
type=str,
help='site name')
parser.add_argument(
'master_key',
type=str,
help='master password')
parser.add_argument(
'-l', '--length',
type=int,
default=10,
help='length of the generated password [max 27]')
parser.add_argument(
'--no-digits',
action='store_false',
dest='require_digit',
help='do not include at least one digit')
parser.add_argument(
'--no-punctuation',
action='store_false',
dest='require_punctuation',
help='do not include at least one punctuation symbol')
parser.add_argument(
'--no-mixedcase',
action='store_false',
dest='require_mixedcase',
help='do not make sure there\'s at least one upper and one lower case char')
parser.add_argument(
'--no-special',
action='store_true',
dest='restrict_special',
help='exclude non-alphanumeric characters')
parser.add_argument(
'--digits-only',
action='store_true',
dest='restrict_digits',
help='generate a digits-only password')
args = parser.parse_args()
print PassHash(args).password
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment