Update to python 3

This commit is contained in:
Pavle Portic 2019-05-29 13:44:26 +02:00
parent 432c3359c1
commit 03a3edbd5b
Signed by: TheEdgeOfRage
GPG Key ID: 6758ACE46AA2A849
7 changed files with 395 additions and 276 deletions

125
.gitignore vendored Normal file
View File

@ -0,0 +1,125 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/

View File

@ -1,6 +0,0 @@
language: python
python:
- "2.7"
- "pypy"
install: python setup.py install
script: nosetests

View File

@ -4,15 +4,16 @@ import sys, os
version = '0.5' version = '0.5'
setup(name='twik', setup(name='twik',
version=version, version=version,
description="Twik is an application that makes it easier to generate secure and different passwords for each website.", description="Twik is an application that makes it easier to generate secure and different passwords for each website.",
keywords='twik password hash', keywords='twik password hash',
author='Alexandre Possebom', author='Alexandre Possebom',
author_email='alexandrepossebom@gmail.com', author_email='alexandrepossebom@gmail.com',
url='https://github.com/coxande/Twik', url='https://github.com/coxande/Twik',
license='GPLv3', license='GPLv3',
packages=['twik'], packages=['twik'],
entry_points = { entry_points = {
'console_scripts': ['twik = twik.run:main'], 'console_scripts': ['twik = twik.run:main'],
}, },
) )

View File

@ -2,73 +2,67 @@ import unittest
from twik import twik from twik import twik
class SimpleTest(unittest.TestCase): class SimpleTest(unittest.TestCase):
def setUp(self): def setUp(self):
self.t = twik.Twik() self.t = twik.Twik()
def testPasswordAphanumericAndSpecialChars(self): def testPasswordAphanumericAndSpecialChars(self):
for chars in range(4, 27): for chars in range(4, 27):
password = self.t.getpassword('tag', password = self.t.getpassword('tag', 'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', chars, 1)
'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', chars, 1) if chars == 4:
if chars == 4: self.assertEqual(password, 'm3/I')
self.assertEqual(password, 'm3/I') if chars == 8:
if chars == 8: self.assertEqual(password, 'mb/5AsJ9')
self.assertEqual(password, 'mb/5AsJ9') if chars == 12:
if chars == 12: self.assertEqual(password, 'mb/5AsJ9Uon7')
self.assertEqual(password, 'mb/5AsJ9Uon7') if chars == 22:
if chars == 22: self.assertEqual(password, 'mb15As*9Uon7ZzvcsXMjpV')
self.assertEqual(password, 'mb15As*9Uon7ZzvcsXMjpV') if chars == 26:
if chars == 26: self.assertEqual(password, 'mb15AsJ9&on7ZzvcsXMjpVLTqQ')
self.assertEqual(password, 'mb15AsJ9&on7ZzvcsXMjpVLTqQ')
def testPasswordAlphanumeric(self): def testPasswordAlphanumeric(self):
for chars in range(4, 27): for chars in range(4, 27):
password = self.t.getpassword('tag', password = self.t.getpassword('tag', 'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', chars, 2)
'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', chars, 2) if chars == 4:
if chars == 4: self.assertEqual(password, 'm31I')
self.assertEqual(password, 'm31I') if chars == 8:
if chars == 8: self.assertEqual(password, 'mb15AsJ9')
self.assertEqual(password, 'mb15AsJ9') if chars == 12:
if chars == 12: self.assertEqual(password, 'mb15AsJ9Uon7')
self.assertEqual(password, 'mb15AsJ9Uon7') if chars == 22:
if chars == 22: self.assertEqual(password, 'mb15AsJ9Uon7ZzvcsXMjpV')
self.assertEqual(password, 'mb15AsJ9Uon7ZzvcsXMjpV') if chars == 26:
if chars == 26: self.assertEqual(password, 'mb15AsJ9Uon7ZzvcsXMjpVLTqQ')
self.assertEqual(password, 'mb15AsJ9Uon7ZzvcsXMjpVLTqQ')
def testPasswordNumeric(self): def testPasswordNumeric(self):
for chars in range(4, 27): for chars in range(4, 27):
password = self.t.getpassword('tag', password = self.t.getpassword('tag', 'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', chars, 3)
'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', chars, 3) if chars == 4:
if chars == 4: self.assertEqual(password, '4315')
self.assertEqual(password, '4315') if chars == 8:
if chars == 8: self.assertEqual(password, '43154099')
self.assertEqual(password, '43154099') if chars == 12:
if chars == 12: self.assertEqual(password, '431540992657')
self.assertEqual(password, '431540992657') if chars == 22:
if chars == 22: self.assertEqual(password, '4315409926570734032171')
self.assertEqual(password, '4315409926570734032171') if chars == 26:
if chars == 26: self.assertEqual(password, '43154099265707340321711986')
self.assertEqual(password, '43154099265707340321711986')
def testSize(self): def testSize(self):
for chars in range(4, 27): for chars in range(4, 27):
password = self.t.getpassword('tag', password = self.t.getpassword('tag', 'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', chars, 1)
'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', chars, 1) self.assertEqual(len(password), chars)
self.assertEqual(len(password), chars)
def testSizeWrong(self): def testSizeWrong(self):
password = self.t.getpassword('tag', password = self.t.getpassword('tag', 'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', 100, 1)
'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', 100, 1) self.assertEqual(password, None)
self.assertEqual(password, None) def testSize(self):
def testSize(self): password = self.t.getpassword('tag', 'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', 0, 1)
password = self.t.getpassword('tag', self.assertEqual(password, None)
'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', 0, 1)
self.assertEqual(password, None)
def tearDown(self): def tearDown(self):
self.t = None self.t = None
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -18,51 +18,53 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Twik. If not, see <http://www.gnu.org/licenses/>. along with Twik. If not, see <http://www.gnu.org/licenses/>.
""" """
from util import Util from .util import Util
from twik import Twik from .twik import Twik
import getpass import getpass
import argparse import argparse
import sys import sys
def main(): def main():
""" """
ALPHANUMERIC_AND_SPECIAL_CHARS=1 ALPHANUMERIC_AND_SPECIAL_CHARS=1
ALPHANUMERIC=2 ALPHANUMERIC=2
NUMERIC=3 NUMERIC=3
""" """
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("tag", type=str, parser.add_argument("tag", type=str,
help="generate password for a specified tag") help="generate password for a specified tag")
parser.add_argument("-c", "--chars", type=int, parser.add_argument("-c", "--chars", type=int,
choices=range(4, 27), choices=list(range(4, 27)),
metavar="[4-26]", metavar="[4-26]",
help="length of generated password [4-26]. Default: 12") help="length of generated password [4-26]. Default: 12")
parser.add_argument("-p", "--profile", type=str, default=None, parser.add_argument("-p", "--profile", type=str, default=None,
help="profile to use. Default:'Personal'") help="profile to use. Default:'Personal'")
parser.add_argument("-t", "--passwordtype", type=int, choices=[1, 2, 3], parser.add_argument("-t", "--passwordtype", type=int, choices=[1, 2, 3],
help=''' help='''
1 for ALPHANUMERIC_AND_SPECIAL_CHAR 1 for ALPHANUMERIC_AND_SPECIAL_CHAR
2 for ALPHANUMERIC 2 for ALPHANUMERIC
3 for NUMERIC 3 for NUMERIC
Default: 1 Default: 1
''') ''')
args = parser.parse_args() args = parser.parse_args()
util = Util(args.tag, args.chars, args.passwordtype, args.profile) util = Util(args.tag, args.chars, args.passwordtype, args.profile)
try: try:
master_key = getpass.getpass(prompt='Master Key: ') master_key = getpass.getpass(prompt='Master Key: ')
except KeyboardInterrupt: except KeyboardInterrupt:
print "^C" print("^C")
raise SystemExit(0) raise SystemExit(0)
twik = Twik() twik = Twik()
password = twik.getpassword(args.tag, util.get_privatekey(), master_key, password = twik.getpassword(args.tag, util.get_privatekey(), master_key,
util.get_chars(), util.get_passord_type()) util.get_chars(), util.get_passord_type())
print "Your password is %s" % password print(password, end='')
print('', file=sys.stderr)
if __name__ == "__main__": if __name__ == "__main__":
main() main()
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View File

@ -24,79 +24,80 @@ You should have received a copy of the GNU General Public License
along with Twik. If not, see <http://www.gnu.org/licenses/>. along with Twik. If not, see <http://www.gnu.org/licenses/>.
""" """
from hashlib import sha1
from util import Util
import hmac
import getpass
import argparse import argparse
import base64
import getpass
import hmac
import sys import sys
from hashlib import sha1
from .util import Util
class Twik(object): class Twik(object):
def injectcharacter(self, mhash, offset, reserved, seed, length, cstart, cnum): def injectcharacter(self, mhash, offset, reserved, seed, length, cstart, cnum):
pos0 = seed % length pos0 = seed % length
pos = (pos0 + offset) % length pos = (pos0 + offset) % length
for i in range(0, length - reserved): for i in range(0, length - reserved):
tmp = (pos0 + reserved + i) % length tmp = (pos0 + reserved + i) % length
char = ord(mhash[tmp]) char = ord(mhash[tmp])
if char >= ord(cstart) and char < ord(cstart) + cnum: if char >= ord(cstart) and char < ord(cstart) + cnum:
return mhash return mhash
head = mhash[:pos] if pos > 0 else "" head = mhash[:pos] if pos > 0 else ''
inject = ((seed + ord(mhash[pos])) % cnum) + ord(cstart) inject = ((seed + ord(mhash[pos])) % cnum) + ord(cstart)
tail = mhash[pos+1:] if (pos + 1 < len(mhash)) else mhash tail = mhash[pos+1:] if (pos + 1 < len(mhash)) else mhash
return head + chr(inject) + tail return head + chr(inject) + tail
def removespecialcharacters(self, mhash, seed, length): def removespecialcharacters(self, mhash, seed, length):
inputchars = list(mhash) inputchars = list(mhash)
pivot = 0 pivot = 0
for i in range(0, length): for i in range(0, length):
if not inputchars[i].isdigit() and not inputchars[i].isalpha(): if not inputchars[i].isdigit() and not inputchars[i].isalpha():
inputchars[i] = chr(((seed + pivot) % 26 + ord('A'))) inputchars[i] = chr(((seed + pivot) % 26 + ord('A')))
pivot = i + 1 pivot = i + 1
return "".join(inputchars) return ''.join(inputchars)
def converttodigits(self, mhash, seed, length): def converttodigits(self, mhash, seed, length):
inputchars = list(mhash) inputchars = list(mhash)
pivot = 0 pivot = 0
for i in range(0, length): for i in range(0, length):
if not inputchars[i].isdigit(): if not inputchars[i].isdigit():
inputchars[i] = chr(((seed + ord(inputchars[pivot])) % 10 + inputchars[i] = chr(((seed + ord(inputchars[pivot])) % 10 +
ord('0'))) ord('0')))
pivot = i + 1 pivot = i + 1
return "".join(inputchars) return ''.join(inputchars)
def generatehash(self, tag, key, length, password_type): def generatehash(self, tag, key, length, password_type):
digest = hmac.new(key, tag, sha1).digest() digest = hmac.new(str.encode(key), str.encode(tag), sha1).digest()
mhash = digest.encode('base64')[:-2] mhash = base64.b64encode(digest)[:-1]
mhash = mhash.decode('utf-8')
seed = sum([ord(i) for i in mhash])
seed = 0 # NUMERIC
for i in range(0, len(mhash)): if password_type == 3:
seed += ord(mhash[i]) mhash = self.converttodigits(mhash, seed, length)
else:
mhash = self.injectcharacter(mhash, 0, 4, seed, length, '0', 10)
# ALPHANUMERIC_AND_SPECIAL_CHARS
if password_type == 1:
mhash = self.injectcharacter(mhash, 1, 4, seed, length, '!', 15)
mhash = self.injectcharacter(mhash, 2, 4, seed, length, 'A', 26)
mhash = self.injectcharacter(mhash, 3, 4, seed, length, 'a', 26)
"""NUMERIC""" # ALPHANUMERIC
if password_type == 3: if password_type == 2:
mhash = self.converttodigits(mhash, seed, length) mhash = self.removespecialcharacters(mhash, seed, length)
else:
mhash = self.injectcharacter(mhash, 0, 4, seed, length, '0', 10)
"""ALPHANUMERIC_AND_SPECIAL_CHARS"""
if password_type == 1:
mhash = self.injectcharacter(mhash, 1, 4, seed, length, '!', 15)
mhash = self.injectcharacter(mhash, 2, 4, seed, length, 'A', 26)
mhash = self.injectcharacter(mhash, 3, 4, seed, length, 'a', 26)
"""ALPHANUMERIC""" return mhash[:length]
if password_type == 2:
mhash = self.removespecialcharacters(mhash, seed, length)
return mhash[:length]
def getpassword(self, tag, private_key, master_key, length, password_type): def getpassword(self, tag, private_key, master_key, length, password_type):
if length > 26 or length < 4: if length > 26 or length < 4:
return None return None
mhash = self.generatehash(private_key, tag, 24, 1) mhash = self.generatehash(private_key, tag, 24, 1)
password = self.generatehash(mhash, master_key, length, password_type) password = self.generatehash(mhash, master_key, length, password_type)
return password return password
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View File

@ -24,123 +24,125 @@ You should have received a copy of the GNU General Public License
along with Twik. If not, see <http://www.gnu.org/licenses/>. along with Twik. If not, see <http://www.gnu.org/licenses/>.
""" """
import configparser
import os.path import os.path
import ConfigParser import sys
from random import SystemRandom from random import SystemRandom
def privatekeygenerator(): def privatekeygenerator():
""" """
Generate new private key Generate new private key
""" """
subgroups_length = [8, 4, 4, 4, 12] subgroups_length = [8, 4, 4, 4, 12]
subgroup_separator = '-' subgroup_separator = '-'
allowed_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" allowed_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
systemrandom = SystemRandom() systemrandom = SystemRandom()
allowedcharslength = len(allowed_chars) allowedcharslength = len(allowed_chars)
key = "" key = ""
for i in range(0, len(subgroups_length)): for i in range(0, len(subgroups_length)):
for j in range(0, subgroups_length[i]): for j in range(0, subgroups_length[i]):
key += allowed_chars[systemrandom.randrange(allowedcharslength)] key += allowed_chars[systemrandom.randrange(allowedcharslength)]
if i < (len(subgroups_length) -1): if i < (len(subgroups_length) -1):
key += subgroup_separator key += subgroup_separator
return key return key
class Util(object): class Util(object):
""" """
Class for deal with config file Class for deal with config file
""" """
def __init__(self, tag, chars, pass_type, profile): def __init__(self, tag, chars, pass_type, profile):
""" """
Constructor Constructor
""" """
homedir = os.path.expanduser('~') homedir = os.path.expanduser('~')
self.filename = os.path.join(homedir, '.twik.conf') self.filename = os.path.join(homedir, '.twik.conf')
self.config = ConfigParser.ConfigParser() self.config = configparser.ConfigParser()
self.config.read(self.filename) self.config.read(self.filename)
self.tag = tag self.tag = tag
self.chars = chars self.chars = chars
self.profile = profile self.profile = profile
self.pass_type = pass_type self.pass_type = pass_type
#Initialize default values #Initialize default values
self.get_privatekey() self.get_privatekey()
def writeconfig(self): def writeconfig(self):
""" """
Write config file Write config file
""" """
with open(self.filename, 'w+') as fileconfig: with open(self.filename, 'w+') as fileconfig:
self.config.write(fileconfig) self.config.write(fileconfig)
def get_privatekey(self): def get_privatekey(self):
""" """
Get private key if not exists create new one Get private key if not exists create new one
""" """
private_key = '' private_key = ''
if self.profile == None and len(self.config.sections()) > 0: if self.profile == None and len(self.config.sections()) > 0:
for session in self.config.sections(): for session in self.config.sections():
if self.config.has_option(session, 'default') and self.config.getboolean(session, 'default') == True: if self.config.has_option(session, 'default') and self.config.getboolean(session, 'default') == True:
self.profile = session self.profile = session
break break
if self.profile == None: if self.profile == None:
self.profile = self.config.sections()[0] self.profile = self.config.sections()[0]
print 'Using profile : %s' % self.profile print(f'Using profile: {self.profile}', file=sys.stderr)
if self.profile and self.config.has_option(self.profile, 'private_key'): if self.profile and self.config.has_option(self.profile, 'private_key'):
private_key = self.config.get(self.profile, 'private_key') private_key = self.config.get(self.profile, 'private_key')
else: else:
private_key = privatekeygenerator() private_key = privatekeygenerator()
if self.profile == None: if self.profile == None:
self.profile = 'Personal' self.profile = 'Personal'
self.config.add_section(self.profile) self.config.add_section(self.profile)
self.config.set(self.profile, 'private_key', private_key) self.config.set(self.profile, 'private_key', private_key)
chars = self.chars chars = self.chars
if chars == None: if chars == None:
chars = 12 chars = 12
pass_type = self.pass_type pass_type = self.pass_type
if pass_type == None: if pass_type == None:
pass_type = 1 pass_type = 1
self.config.set(self.profile, 'chars', chars) self.config.set(self.profile, 'chars', chars)
self.config.set(self.profile, 'password_type', pass_type) self.config.set(self.profile, 'password_type', pass_type)
if self.profile == 'Personal': if self.profile == 'Personal':
self.config.set(self.profile, 'default', 1) self.config.set(self.profile, 'default', 1)
self.writeconfig() self.writeconfig()
print 'New profile is generated' print('New profile is generated')
self.config.read(self.filename) self.config.read(self.filename)
return private_key return private_key
def get_chars(self): def get_chars(self):
config_key = '%s_chars' % self.tag config_key = f'{self.tag}_chars'
if self.config.has_option(self.profile, config_key) and self.chars == None: if self.config.has_option(self.profile, config_key) and self.chars == None:
self.chars = self.config.getint(self.profile, config_key) self.chars = self.config.getint(self.profile, config_key)
else: else:
if self.chars == None and self.config.has_option(self.profile, 'chars'): if self.chars == None and self.config.has_option(self.profile, 'chars'):
self.chars = self.config.getint(self.profile, 'chars') self.chars = self.config.getint(self.profile, 'chars')
self.config.set(self.profile, config_key, self.chars) self.config.set(self.profile, config_key, str(self.chars))
self.writeconfig() self.writeconfig()
if self.chars < 4 or self.chars > 26: if self.chars < 4 or self.chars > 26:
print 'invalid password length value from configuration using default' print('invalid password length value from configuration using default')
self.chars = 12 self.chars = 12
return self.chars return self.chars
def get_passord_type(self): def get_passord_type(self):
config_key = '%s_password_type' % self.tag config_key = f'{self.tag}_password_type'
if self.config.has_option(self.profile, config_key) and self.pass_type == None: if self.config.has_option(self.profile, config_key) and self.pass_type == None:
self.pass_type = self.config.getint(self.profile, config_key) self.pass_type = self.config.getint(self.profile, config_key)
else: else:
if self.pass_type == None and self.config.has_option(self.profile, 'password_type'): if self.pass_type == None and self.config.has_option(self.profile, 'password_type'):
self.pass_type = self.config.getint(self.profile, 'password_type') self.pass_type = self.config.getint(self.profile, 'password_type')
self.config.set(self.profile, config_key, self.pass_type) self.config.set(self.profile, config_key, str(self.pass_type))
self.writeconfig() self.writeconfig()
if self.pass_type < 1 or self.pass_type > 3: if self.pass_type < 1 or self.pass_type > 3:
print 'invalid password type value from configuration using default' print('invalid password type value from configuration using default')
self.pass_type = 1 self.pass_type = 1
return self.pass_type return self.pass_type
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4