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'
setup(name='twik',
version=version,
description="Twik is an application that makes it easier to generate secure and different passwords for each website.",
keywords='twik password hash',
author='Alexandre Possebom',
author_email='alexandrepossebom@gmail.com',
url='https://github.com/coxande/Twik',
license='GPLv3',
packages=['twik'],
entry_points = {
'console_scripts': ['twik = twik.run:main'],
},
)
version=version,
description="Twik is an application that makes it easier to generate secure and different passwords for each website.",
keywords='twik password hash',
author='Alexandre Possebom',
author_email='alexandrepossebom@gmail.com',
url='https://github.com/coxande/Twik',
license='GPLv3',
packages=['twik'],
entry_points = {
'console_scripts': ['twik = twik.run:main'],
},
)

View File

@ -2,73 +2,67 @@ import unittest
from twik import twik
class SimpleTest(unittest.TestCase):
def setUp(self):
self.t = twik.Twik()
def setUp(self):
self.t = twik.Twik()
def testPasswordAphanumericAndSpecialChars(self):
for chars in range(4, 27):
password = self.t.getpassword('tag',
'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', chars, 1)
if chars == 4:
self.assertEqual(password, 'm3/I')
if chars == 8:
self.assertEqual(password, 'mb/5AsJ9')
if chars == 12:
self.assertEqual(password, 'mb/5AsJ9Uon7')
if chars == 22:
self.assertEqual(password, 'mb15As*9Uon7ZzvcsXMjpV')
if chars == 26:
self.assertEqual(password, 'mb15AsJ9&on7ZzvcsXMjpVLTqQ')
def testPasswordAphanumericAndSpecialChars(self):
for chars in range(4, 27):
password = self.t.getpassword('tag', 'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', chars, 1)
if chars == 4:
self.assertEqual(password, 'm3/I')
if chars == 8:
self.assertEqual(password, 'mb/5AsJ9')
if chars == 12:
self.assertEqual(password, 'mb/5AsJ9Uon7')
if chars == 22:
self.assertEqual(password, 'mb15As*9Uon7ZzvcsXMjpV')
if chars == 26:
self.assertEqual(password, 'mb15AsJ9&on7ZzvcsXMjpVLTqQ')
def testPasswordAlphanumeric(self):
for chars in range(4, 27):
password = self.t.getpassword('tag',
'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', chars, 2)
if chars == 4:
self.assertEqual(password, 'm31I')
if chars == 8:
self.assertEqual(password, 'mb15AsJ9')
if chars == 12:
self.assertEqual(password, 'mb15AsJ9Uon7')
if chars == 22:
self.assertEqual(password, 'mb15AsJ9Uon7ZzvcsXMjpV')
if chars == 26:
self.assertEqual(password, 'mb15AsJ9Uon7ZzvcsXMjpVLTqQ')
def testPasswordAlphanumeric(self):
for chars in range(4, 27):
password = self.t.getpassword('tag', 'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', chars, 2)
if chars == 4:
self.assertEqual(password, 'm31I')
if chars == 8:
self.assertEqual(password, 'mb15AsJ9')
if chars == 12:
self.assertEqual(password, 'mb15AsJ9Uon7')
if chars == 22:
self.assertEqual(password, 'mb15AsJ9Uon7ZzvcsXMjpV')
if chars == 26:
self.assertEqual(password, 'mb15AsJ9Uon7ZzvcsXMjpVLTqQ')
def testPasswordNumeric(self):
for chars in range(4, 27):
password = self.t.getpassword('tag',
'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', chars, 3)
if chars == 4:
self.assertEqual(password, '4315')
if chars == 8:
self.assertEqual(password, '43154099')
if chars == 12:
self.assertEqual(password, '431540992657')
if chars == 22:
self.assertEqual(password, '4315409926570734032171')
if chars == 26:
self.assertEqual(password, '43154099265707340321711986')
def testPasswordNumeric(self):
for chars in range(4, 27):
password = self.t.getpassword('tag', 'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', chars, 3)
if chars == 4:
self.assertEqual(password, '4315')
if chars == 8:
self.assertEqual(password, '43154099')
if chars == 12:
self.assertEqual(password, '431540992657')
if chars == 22:
self.assertEqual(password, '4315409926570734032171')
if chars == 26:
self.assertEqual(password, '43154099265707340321711986')
def testSize(self):
for chars in range(4, 27):
password = self.t.getpassword('tag',
'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', chars, 1)
self.assertEqual(len(password), chars)
def testSize(self):
for chars in range(4, 27):
password = self.t.getpassword('tag', 'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', chars, 1)
self.assertEqual(len(password), chars)
def testSizeWrong(self):
password = self.t.getpassword('tag',
'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', 100, 1)
self.assertEqual(password, None)
def testSize(self):
password = self.t.getpassword('tag',
'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', 0, 1)
self.assertEqual(password, None)
def testSizeWrong(self):
password = self.t.getpassword('tag', 'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', 100, 1)
self.assertEqual(password, None)
def testSize(self):
password = self.t.getpassword('tag', 'TFCY2AJI-NBPU-V01E-F7CP-PJIZNRKPF25W', 'foobar', 0, 1)
self.assertEqual(password, None)
def tearDown(self):
self.t = None
def tearDown(self):
self.t = None
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
along with Twik. If not, see <http://www.gnu.org/licenses/>.
"""
from util import Util
from twik import Twik
from .util import Util
from .twik import Twik
import getpass
import argparse
import sys
def main():
"""
ALPHANUMERIC_AND_SPECIAL_CHARS=1
ALPHANUMERIC=2
NUMERIC=3
"""
parser = argparse.ArgumentParser()
parser.add_argument("tag", type=str,
help="generate password for a specified tag")
parser.add_argument("-c", "--chars", type=int,
choices=range(4, 27),
metavar="[4-26]",
help="length of generated password [4-26]. Default: 12")
parser.add_argument("-p", "--profile", type=str, default=None,
help="profile to use. Default:'Personal'")
parser.add_argument("-t", "--passwordtype", type=int, choices=[1, 2, 3],
help='''
1 for ALPHANUMERIC_AND_SPECIAL_CHAR
2 for ALPHANUMERIC
3 for NUMERIC
Default: 1
''')
args = parser.parse_args()
"""
ALPHANUMERIC_AND_SPECIAL_CHARS=1
ALPHANUMERIC=2
NUMERIC=3
"""
parser = argparse.ArgumentParser()
parser.add_argument("tag", type=str,
help="generate password for a specified tag")
parser.add_argument("-c", "--chars", type=int,
choices=list(range(4, 27)),
metavar="[4-26]",
help="length of generated password [4-26]. Default: 12")
parser.add_argument("-p", "--profile", type=str, default=None,
help="profile to use. Default:'Personal'")
parser.add_argument("-t", "--passwordtype", type=int, choices=[1, 2, 3],
help='''
1 for ALPHANUMERIC_AND_SPECIAL_CHAR
2 for ALPHANUMERIC
3 for NUMERIC
Default: 1
''')
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:
master_key = getpass.getpass(prompt='Master Key: ')
except KeyboardInterrupt:
print "^C"
raise SystemExit(0)
try:
master_key = getpass.getpass(prompt='Master Key: ')
except KeyboardInterrupt:
print("^C")
raise SystemExit(0)
twik = Twik()
password = twik.getpassword(args.tag, util.get_privatekey(), master_key,
util.get_chars(), util.get_passord_type())
twik = Twik()
password = twik.getpassword(args.tag, util.get_privatekey(), master_key,
util.get_chars(), util.get_passord_type())
print "Your password is %s" % password
print(password, end='')
print('', file=sys.stderr)
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/>.
"""
from hashlib import sha1
from util import Util
import hmac
import getpass
import argparse
import base64
import getpass
import hmac
import sys
from hashlib import sha1
from .util import Util
class Twik(object):
def injectcharacter(self, mhash, offset, reserved, seed, length, cstart, cnum):
pos0 = seed % length
pos = (pos0 + offset) % length
for i in range(0, length - reserved):
tmp = (pos0 + reserved + i) % length
char = ord(mhash[tmp])
if char >= ord(cstart) and char < ord(cstart) + cnum:
return mhash
head = mhash[:pos] if pos > 0 else ""
inject = ((seed + ord(mhash[pos])) % cnum) + ord(cstart)
tail = mhash[pos+1:] if (pos + 1 < len(mhash)) else mhash
return head + chr(inject) + tail
def injectcharacter(self, mhash, offset, reserved, seed, length, cstart, cnum):
pos0 = seed % length
pos = (pos0 + offset) % length
for i in range(0, length - reserved):
tmp = (pos0 + reserved + i) % length
char = ord(mhash[tmp])
if char >= ord(cstart) and char < ord(cstart) + cnum:
return mhash
head = mhash[:pos] if pos > 0 else ''
inject = ((seed + ord(mhash[pos])) % cnum) + ord(cstart)
tail = mhash[pos+1:] if (pos + 1 < len(mhash)) else mhash
return head + chr(inject) + tail
def removespecialcharacters(self, mhash, seed, length):
inputchars = list(mhash)
pivot = 0
for i in range(0, length):
if not inputchars[i].isdigit() and not inputchars[i].isalpha():
inputchars[i] = chr(((seed + pivot) % 26 + ord('A')))
pivot = i + 1
return "".join(inputchars)
def removespecialcharacters(self, mhash, seed, length):
inputchars = list(mhash)
pivot = 0
for i in range(0, length):
if not inputchars[i].isdigit() and not inputchars[i].isalpha():
inputchars[i] = chr(((seed + pivot) % 26 + ord('A')))
pivot = i + 1
return ''.join(inputchars)
def converttodigits(self, mhash, seed, length):
inputchars = list(mhash)
pivot = 0
for i in range(0, length):
if not inputchars[i].isdigit():
inputchars[i] = chr(((seed + ord(inputchars[pivot])) % 10 +
ord('0')))
pivot = i + 1
return "".join(inputchars)
def converttodigits(self, mhash, seed, length):
inputchars = list(mhash)
pivot = 0
for i in range(0, length):
if not inputchars[i].isdigit():
inputchars[i] = chr(((seed + ord(inputchars[pivot])) % 10 +
ord('0')))
pivot = i + 1
return ''.join(inputchars)
def generatehash(self, tag, key, length, password_type):
digest = hmac.new(key, tag, sha1).digest()
mhash = digest.encode('base64')[:-2]
def generatehash(self, tag, key, length, password_type):
digest = hmac.new(str.encode(key), str.encode(tag), sha1).digest()
mhash = base64.b64encode(digest)[:-1]
mhash = mhash.decode('utf-8')
seed = sum([ord(i) for i in mhash])
seed = 0
for i in range(0, len(mhash)):
seed += ord(mhash[i])
# NUMERIC
if password_type == 3:
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"""
if password_type == 3:
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)
# ALPHANUMERIC
if password_type == 2:
mhash = self.removespecialcharacters(mhash, seed, length)
"""ALPHANUMERIC"""
if password_type == 2:
mhash = self.removespecialcharacters(mhash, seed, length)
return mhash[:length]
return mhash[:length]
def getpassword(self, tag, private_key, master_key, length, password_type):
if length > 26 or length < 4:
return None
mhash = self.generatehash(private_key, tag, 24, 1)
password = self.generatehash(mhash, master_key, length, password_type)
return password
def getpassword(self, tag, private_key, master_key, length, password_type):
if length > 26 or length < 4:
return None
mhash = self.generatehash(private_key, tag, 24, 1)
password = self.generatehash(mhash, master_key, length, password_type)
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/>.
"""
import configparser
import os.path
import ConfigParser
import sys
from random import SystemRandom
def privatekeygenerator():
"""
Generate new private key
"""
subgroups_length = [8, 4, 4, 4, 12]
subgroup_separator = '-'
allowed_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
systemrandom = SystemRandom()
allowedcharslength = len(allowed_chars)
key = ""
for i in range(0, len(subgroups_length)):
for j in range(0, subgroups_length[i]):
key += allowed_chars[systemrandom.randrange(allowedcharslength)]
if i < (len(subgroups_length) -1):
key += subgroup_separator
return key
"""
Generate new private key
"""
subgroups_length = [8, 4, 4, 4, 12]
subgroup_separator = '-'
allowed_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
systemrandom = SystemRandom()
allowedcharslength = len(allowed_chars)
key = ""
for i in range(0, len(subgroups_length)):
for j in range(0, subgroups_length[i]):
key += allowed_chars[systemrandom.randrange(allowedcharslength)]
if i < (len(subgroups_length) -1):
key += subgroup_separator
return key
class Util(object):
"""
Class for deal with config file
"""
def __init__(self, tag, chars, pass_type, profile):
"""
Constructor
"""
homedir = os.path.expanduser('~')
self.filename = os.path.join(homedir, '.twik.conf')
self.config = ConfigParser.ConfigParser()
self.config.read(self.filename)
self.tag = tag
self.chars = chars
self.profile = profile
self.pass_type = pass_type
#Initialize default values
self.get_privatekey()
"""
Class for deal with config file
"""
def __init__(self, tag, chars, pass_type, profile):
"""
Constructor
"""
homedir = os.path.expanduser('~')
self.filename = os.path.join(homedir, '.twik.conf')
self.config = configparser.ConfigParser()
self.config.read(self.filename)
self.tag = tag
self.chars = chars
self.profile = profile
self.pass_type = pass_type
#Initialize default values
self.get_privatekey()
def writeconfig(self):
"""
Write config file
"""
with open(self.filename, 'w+') as fileconfig:
self.config.write(fileconfig)
def writeconfig(self):
"""
Write config file
"""
with open(self.filename, 'w+') as fileconfig:
self.config.write(fileconfig)
def get_privatekey(self):
"""
Get private key if not exists create new one
"""
private_key = ''
if self.profile == None and len(self.config.sections()) > 0:
for session in self.config.sections():
if self.config.has_option(session, 'default') and self.config.getboolean(session, 'default') == True:
self.profile = session
break
if self.profile == None:
self.profile = self.config.sections()[0]
print 'Using profile : %s' % self.profile
def get_privatekey(self):
"""
Get private key if not exists create new one
"""
private_key = ''
if self.profile == None and len(self.config.sections()) > 0:
for session in self.config.sections():
if self.config.has_option(session, 'default') and self.config.getboolean(session, 'default') == True:
self.profile = session
break
if self.profile == None:
self.profile = self.config.sections()[0]
print(f'Using profile: {self.profile}', file=sys.stderr)
if self.profile and self.config.has_option(self.profile, 'private_key'):
private_key = self.config.get(self.profile, 'private_key')
else:
private_key = privatekeygenerator()
if self.profile == None:
self.profile = 'Personal'
self.config.add_section(self.profile)
self.config.set(self.profile, 'private_key', private_key)
chars = self.chars
if chars == None:
chars = 12
pass_type = self.pass_type
if pass_type == None:
pass_type = 1
self.config.set(self.profile, 'chars', chars)
self.config.set(self.profile, 'password_type', pass_type)
if self.profile == 'Personal':
self.config.set(self.profile, 'default', 1)
self.writeconfig()
print 'New profile is generated'
self.config.read(self.filename)
return private_key
if self.profile and self.config.has_option(self.profile, 'private_key'):
private_key = self.config.get(self.profile, 'private_key')
else:
private_key = privatekeygenerator()
if self.profile == None:
self.profile = 'Personal'
self.config.add_section(self.profile)
self.config.set(self.profile, 'private_key', private_key)
chars = self.chars
if chars == None:
chars = 12
pass_type = self.pass_type
if pass_type == None:
pass_type = 1
self.config.set(self.profile, 'chars', chars)
self.config.set(self.profile, 'password_type', pass_type)
if self.profile == 'Personal':
self.config.set(self.profile, 'default', 1)
self.writeconfig()
print('New profile is generated')
self.config.read(self.filename)
return private_key
def get_chars(self):
config_key = '%s_chars' % self.tag
def get_chars(self):
config_key = f'{self.tag}_chars'
if self.config.has_option(self.profile, config_key) and self.chars == None:
self.chars = self.config.getint(self.profile, config_key)
else:
if self.chars == None and self.config.has_option(self.profile, 'chars'):
self.chars = self.config.getint(self.profile, 'chars')
self.config.set(self.profile, config_key, self.chars)
self.writeconfig()
if self.config.has_option(self.profile, config_key) and self.chars == None:
self.chars = self.config.getint(self.profile, config_key)
else:
if self.chars == None and self.config.has_option(self.profile, 'chars'):
self.chars = self.config.getint(self.profile, 'chars')
self.config.set(self.profile, config_key, str(self.chars))
self.writeconfig()
if self.chars < 4 or self.chars > 26:
print 'invalid password length value from configuration using default'
self.chars = 12
if self.chars < 4 or self.chars > 26:
print('invalid password length value from configuration using default')
self.chars = 12
return self.chars
return self.chars
def get_passord_type(self):
config_key = '%s_password_type' % self.tag
def get_passord_type(self):
config_key = f'{self.tag}_password_type'
if self.config.has_option(self.profile, config_key) and self.pass_type == None:
self.pass_type = self.config.getint(self.profile, config_key)
else:
if self.pass_type == None and self.config.has_option(self.profile, 'password_type'):
self.pass_type = self.config.getint(self.profile, 'password_type')
if self.config.has_option(self.profile, config_key) and self.pass_type == None:
self.pass_type = self.config.getint(self.profile, config_key)
else:
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.config.set(self.profile, config_key, self.pass_type)
self.writeconfig()
self.config.set(self.profile, config_key, str(self.pass_type))
self.writeconfig()
if self.pass_type < 1 or self.pass_type > 3:
print 'invalid password type value from configuration using default'
self.pass_type = 1
if self.pass_type < 1 or self.pass_type > 3:
print('invalid password type value from configuration using default')
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