Access to your passwords anywhere and anytime without saving anything!
https://hrypton.ir/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
256 lines
7.4 KiB
256 lines
7.4 KiB
import os |
|
import hashlib |
|
import getpass |
|
from sys import argv as sys_argv |
|
|
|
try: |
|
from colorama import Fore, Style |
|
lite_mode = False |
|
|
|
except ModuleNotFoundError: |
|
print("Warning: We use 'colorama' library to print colored text.\n" |
|
+ "Until it is not installed, all text print with no color.\n" |
|
+ "You can install it using 'pip3 install colorama' command.") |
|
input("Press ENTER to continue") |
|
print("") |
|
lite_mode = True |
|
|
|
|
|
HASH_LAYERS_COUNT = 50_000 # Increase this make algorithm slower (and safer) |
|
PASSWORD_MIN_SIZE = 12 # If you want to increase PASSWORD_MIN_SIZE use sha-512 |
|
|
|
hexChars = "0123456789abcdef" |
|
digits = "0123456789" |
|
lowers = "abcdefghijkmnlopqrstuvwxyz" |
|
uppers = "ABCDEFGHIJKMNLPOQRSTUVWXYZ" |
|
specials = "/~!@#$%^&*_-+=`|\\(){}[]:;\"'<>,.?" |
|
allowedChars = digits + lowers + uppers + specials |
|
home_dir = os.path.expanduser("~") |
|
|
|
|
|
# Generate colored text |
|
def green(text): |
|
if lite_mode == False: |
|
return Fore.GREEN + text + Style.RESET_ALL |
|
|
|
return text |
|
|
|
def red(text): |
|
if lite_mode == False: |
|
return Fore.RED + text + Style.RESET_ALL |
|
|
|
return text |
|
|
|
def bright(text): |
|
if lite_mode == False: |
|
return Style.BRIGHT + text + Style.RESET_ALL |
|
|
|
return text |
|
|
|
|
|
# Clear the screen |
|
def clear(): |
|
os.system("cls" if os.name == "nt" else "clear") |
|
|
|
|
|
# Hashing |
|
def sha256(message): |
|
# encode as UTF-8 |
|
msgBuffer = message.encode('UTF-8') |
|
|
|
# hash the message |
|
hashBuffer = hashlib.sha256(msgBuffer) |
|
|
|
# convert bytes to hex string |
|
hashHex = hashBuffer.hexdigest() |
|
|
|
return hashHex |
|
|
|
|
|
# Repeat hashing |
|
def slowIt(input): |
|
i = 0 |
|
while i <= HASH_LAYERS_COUNT: |
|
input = sha256(input) |
|
i += 1 |
|
|
|
return input |
|
|
|
|
|
# Check for validation of generated password |
|
def validator(password): |
|
hasDigits = False |
|
hasLowers = False |
|
hasUppers = False |
|
hasSpecials = False |
|
|
|
# Check for digits |
|
for digit in digits: |
|
if digit in password: |
|
hasDigits = True |
|
break |
|
|
|
# Check for lower cases |
|
for lower in lowers: |
|
if lower in password: |
|
hasLowers = True |
|
break |
|
|
|
# Check for upper cases |
|
for upper in uppers: |
|
if upper in password: |
|
hasUppers = True |
|
break |
|
|
|
# Check for special charecters |
|
for special in specials: |
|
if special in password: |
|
hasSpecials = True |
|
break |
|
|
|
if hasDigits and hasLowers and hasUppers and hasSpecials: |
|
return True |
|
else: |
|
return False |
|
|
|
|
|
# Return a number for each hex charecter |
|
def charsToNum(input1, input2): |
|
if input1 == "0": return hexChars.index(input2) |
|
if input1 == "1": return hexChars.index(input2) |
|
if input1 == "2": return hexChars.index(input2) + 16 |
|
if input1 == "3": return hexChars.index(input2) + 16 |
|
if input1 == "4": return hexChars.index(input2) + 32 |
|
if input1 == "5": return hexChars.index(input2) + 32 |
|
if input1 == "6": return hexChars.index(input2) + 48 |
|
if input1 == "7": return hexChars.index(input2) + 48 |
|
if input1 == "8": return hexChars.index(input2) + 64 |
|
if input1 == "9": return hexChars.index(input2) + 64 |
|
if input1 == "a": return hexChars.index(input2) + 80 |
|
if input1 == "b": return hexChars.index(input2) + 80 |
|
if input1 == "c": return hexChars.index(input2) + 96 |
|
if input1 == "d": return hexChars.index(input2) + 96 |
|
if input1 == "e": return hexChars.index(input2) + 112 |
|
if input1 == "f": return hexChars.index(input2) + 112 |
|
return "not allowed input1: " + input1 |
|
|
|
|
|
# Switch hex charecters with an allowed charecter |
|
def switch(input1, input2): |
|
number = int(charsToNum(input1, input2)) |
|
# num is a number 0 <= x <= 127 |
|
# so make it a number 0 <= x <= allowedChars.size() |
|
number = int(number * (len(allowedChars) - 1) / 127) |
|
return allowedChars[number] |
|
|
|
|
|
# Give hash chars to switch() function one by one, then to validator() function |
|
def standard(input): |
|
result = "" |
|
|
|
i = 0 |
|
while i <= 63: |
|
result += switch(input[i], input[i+1]) |
|
i += 2 |
|
|
|
# if its not standard try one more hash |
|
# (to make sure output is standard password |
|
# and website validator gonna accept it) |
|
if validator(result[0:PASSWORD_MIN_SIZE+1]) != True: |
|
result = sha256(result) |
|
result = standard(result) |
|
|
|
return result |
|
|
|
|
|
# Decrease generated password length |
|
def limitIt(input): |
|
if input[len(input) - 1] in lowers: bit1 = True |
|
elif input[len(input) - 1] in digits: bit1 = True |
|
else: bit1 = False |
|
|
|
if input[len(input) - 2] in lowers: bit2 = True |
|
elif input[len(input) - 2] in digits: bit2 = True |
|
else: bit2 = False |
|
|
|
if bit1 and bit2: return input[0:PASSWORD_MIN_SIZE] |
|
elif not bit1 and bit2: return input[0:PASSWORD_MIN_SIZE + 1] |
|
elif bit1 and not bit2: return input[0:PASSWORD_MIN_SIZE + 2] |
|
elif not bit1 and not bit2: return input[0:PASSWORD_MIN_SIZE + 3] |
|
|
|
|
|
# Get data and return the password |
|
def pash(masterPass, url, username="", howManyTimeChanged="0"): |
|
value = masterPass + url + username |
|
|
|
if int(howManyTimeChanged) != 0: |
|
value += howManyTimeChanged |
|
|
|
pash = slowIt(value) # Hash the value 50_000 times |
|
pash = standard(pash) # Generate password |
|
pash = limitIt(pash) # Decrease password length |
|
|
|
return pash |
|
|
|
|
|
# pash -> (slowIt <-> sha256) * 50_000 |
|
# -> (starndard <-> switch <-> charsToNum) Repeat until password... |
|
# -> validator (If valid) -> limitIt ...become generated |
|
# (else) -> standard |
|
|
|
# The main function |
|
def give_password(): |
|
masterPass = getpass.getpass("Enter your Master Password: ") |
|
|
|
# Check if master password hash has saved before |
|
if os.path.exists(f"{home_dir}/.Hrypton-py.txt"): |
|
with open(f"{home_dir}/.Hrypton-py.txt", "r") as masterPassHash: |
|
if sha256(masterPass) != masterPassHash.read(): |
|
print(bright(red("Err: ")) + "That's incorrect!\n") |
|
give_password() |
|
|
|
# Request passwrod repeat and then save it hash |
|
else: |
|
masterPassRepeat = getpass.getpass("Enter your Master Password again: ") |
|
|
|
if masterPass == masterPassRepeat: |
|
with open(f"{home_dir}/.Hrypton-py.txt", "w") as masterPassHash: |
|
masterPassHash.write(sha256(masterPass)) |
|
|
|
print("Your master password hash saved in '~/.Hryton-py.txt'") |
|
|
|
else: |
|
print(bright(red("Err: ")) + "They are not match!\n") |
|
give_password() |
|
|
|
print("") |
|
|
|
if len(sys_argv) == 2: |
|
password = pash(masterPass, sys_argv[1]) |
|
if len(sys_argv) == 3: |
|
password = pash(masterPass, sys_argv[1], sys_argv[2]) |
|
if len(sys_argv) == 4: |
|
password = pash(masterPass, sys_argv[1], sys_argv[2], sys_argv[3]) |
|
|
|
# Print the password |
|
print(f" {'*' * (len(password) + 4)}") |
|
print(f"Your password is: * {green(password)} *") |
|
print(f" {'*' * (len(password) + 4)}\n") |
|
|
|
input("Press ENTER to exit and clear the screen!") |
|
clear() |
|
|
|
|
|
# Main |
|
if not os.path.exists(f"{home_dir}/.Hrypton-py"): |
|
os.mkdir(f"{home_dir}/.Hrypton-py") |
|
|
|
# If there is no problem with args |
|
if (len(sys_argv) >= 2) and (len(sys_argv)<= 4): |
|
give_password() |
|
|
|
# If args are more or fewer than it must |
|
else: |
|
print("You must give args like this:") |
|
print("hrypton <URL> <Username> <How_many_time_changed>\n") |
|
print("'Username' and 'How many time changed' are optional.")
|
|
|