Compare commits

..

No commits in common. "fc147deb89a23c79935df7e41b4412facc931c6a" and "2b7709c2aed3eab22f213834bfa95677d3998d37" have entirely different histories.

2 changed files with 99 additions and 74 deletions

View File

@ -115,8 +115,11 @@ def credentials_ini_fmt(k, v):
def credentials_ini(label, setting): def credentials_ini(label, setting):
r = "[{}]\n".format(label) r = "[{}]\n".format(label)
for k, v in setting.items(): dictitems = getattr(type(setting), "items", None)
r.append("{}={}\n".format(k, v)) if dictitems and callable(dictitems):
r += "\n".join([credentials_ini_fmt(k, v) for k, v in setting.items()])
else:
r += credentials_ini_fmt(label, str(setting))
return r return r
@ -130,7 +133,6 @@ def main():
epilog="", epilog="",
add_help=True) add_help=True)
parser.add_argument("-c", "--config", help="Use time localization settings from a pyhton config file (omit the .py extension)", default=None) parser.add_argument("-c", "--config", help="Use time localization settings from a pyhton config file (omit the .py extension)", default=None)
parser.add_argument("-k", "--keyfile", help="Keyfile used for decryption", default=None)
parser.add_argument("--plain", help="Output credentials in plain ini format", action="store_true") parser.add_argument("--plain", help="Output credentials in plain ini format", action="store_true")
parser.add_argument("--minimal", help="Only print OAuth credentials, ignoring encryption settings", action="store_true") parser.add_argument("--minimal", help="Only print OAuth credentials, ignoring encryption settings", action="store_true")
arguments = parser.parse_args() arguments = parser.parse_args()
@ -180,15 +182,11 @@ def main():
ans = _input("Do you want to encrypt your credentials? (y/n)", default("encrypt")) ans = _input("Do you want to encrypt your credentials? (y/n)", default("encrypt"))
if ans.upper() in ("Y", "YES"): if ans.upper() in ("Y", "YES"):
encrypt = True encrypt = True
salt, settings_server = encryption.settings_server_encrypt(settings_server, arguments.keyfile) salt, settings_server = encryption.settings_server_encrypt_cfg(settings_server)
settings_encrypt = OrderedDict([ settings_encrypt = OrderedDict([
("encrypt", encrypt), ("encrypt", encrypt),
("salt", salt), ("salt", salt),
("keyfile", arguments.keyfile)
])
settings_reminder = OrderedDict([
("settings_reminder", reminder)
]) ])
# Credential formatting functions # Credential formatting functions
@ -198,7 +196,7 @@ def main():
print("Copy to your config file!!!") print("Copy to your config file!!!")
print(formatted_credentials("settings_server", settings_server)) print(formatted_credentials("settings_server", settings_server))
if not arguments.minimal: if not arguments.minimal:
print("\n{}\n".format(formatted_credentials("settings_reminder", settings_reminder))) print("\n{}\n".format(formatted_credentials("settings_reminder", reminder)))
print(formatted_credentials("settings_encrypt", settings_encrypt)) print(formatted_credentials("settings_encrypt", settings_encrypt))
print("Success :)") print("Success :)")

View File

@ -28,13 +28,15 @@ from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from collections import OrderedDict from collections import OrderedDict
class EncryptionFail(Exception):
pass
class PasswordMismatch(Exception): class PasswordMismatch(Exception):
pass pass
# Salts
def generate_salt():
return os.urandom(16)
# Return string from bytes # Return string from bytes
def salt_encode(b): def salt_encode(b):
return base64.urlsafe_b64encode(b).decode() return base64.urlsafe_b64encode(b).decode()
@ -70,7 +72,8 @@ def derive_key(password, salt):
iterations=100000, iterations=100000,
backend=default_backend() backend=default_backend()
) )
return base64.urlsafe_b64encode(kdf.derive(password)) r_key = base64.urlsafe_b64encode(kdf.derive(password))
return r_key
# Encryption functions # Encryption functions
@ -96,62 +99,94 @@ def decrypt(token, key):
# encryption_function: encrypt(message, key) : decrypt(token, key): # encryption_function: encrypt(message, key) : decrypt(token, key):
# Returns settings_server_decrypted dictionary with Byte() values. Will need to use # Returns settings_server_decrypted dictionary with Byte() values. Will need to use
# ChangeEncodingDict to make them strings (recommended cfg file friendly) # ChangeEncodingDict to make them strings (recommended cfg file friendly)
def encrypt_settings(settings_server, password, salt, encryption_function): def __settings_server(password, salt, settings_server, encryption_function):
key = derive_key(password, salt) key = derive_key(password, salt)
settings_server_decrypted = OrderedDict() settings_server_decrypted = OrderedDict()
for setting in settings_server: for setting in settings_server:
settings_server_decrypted[setting] = encryption_function(settings_server[setting], key) settings_server_decrypted[setting] = encryption_function(settings_server[setting], key)
return settings_server_decrypted return settings_server_decrypted
def get_keyfile(keyfile=None):
if keyfile is not None:
with open(keyfile, "rb") as f:
return f.read()
return None
# Returns (salt, settings_server)
def _settings_server_encrypt(settings_server):
salt = generate_salt()
password = getpass.getpass("Enter password: ")
password2 = getpass.getpass("Retype Password: ")
def get_pass(q):
try:
return getpass.getpass(q).encode()
except KeyboardInterrupt:
raise EncryptionFail("\nQuitting...")
def settings_server_encrypt(settings_server, keyfile=None):
try:
settings_server = encode_dict(settings_server)
salt = os.urandom(16)
password = get_keyfile(keyfile)
if password is None:
password = get_pass("Enter password: ")
password2 = get_pass("Enter password: ")
if password != password2: if password != password2:
raise PasswordMismatch("Passwords do not match") raise PasswordMismatch
encrypted = encrypt_settings(settings_server, password, salt, encrypt)
return salt_encode(salt), decode_dict(encrypted) settings_server_encrypted = __settings_server(password.encode(), salt, encode_dict(settings_server), encrypt)
except PasswordMismatch as e:
raise EncryptionFail(str(e)) return salt, settings_server_encrypted
except Exception as e:
err = str(e)
raise EncryptionFail("Encrypt Error: {}".format(err))
def settings_server_decrypt(settings_server, settings_encrypt, keyfile=None): # Returns (settings_server)
try: def _settings_server_decrypt(settings_server, settings_encrypt):
if not settings_encrypt["encrypt"]: settings_server_encoded = encode_dict(settings_server)
return settings_server if settings_encrypt["encrypt"]:
settings_server = encode_dict(settings_server)
password = get_keyfile(keyfile or settings_encrypt["keyfile"]) or get_pass("Enter password: ")
salt = salt_decode(settings_encrypt["salt"]) salt = salt_decode(settings_encrypt["salt"])
decrypted = encrypt_settings(settings_server, password, salt, decrypt) password = getpass.getpass("Enter password: ")
return decode_dict(decrypted) return __settings_server(password.encode(), salt, settings_server_encoded, decrypt)
except base64.binascii.Error: else:
raise EncryptionFail("Salt is invalid") return settings_server_encoded
# Wrapper function that will catch exceptions and exit
def settings_server_new(function, **kwargs):
try:
return function(**kwargs)
# If the user cancels the login
except KeyboardInterrupt:
print("\nQuitting...")
# If the user passwords do not match (encrypt)
except PasswordMismatch:
print("Passwords do not match...")
# Incorrect password entered (decrypt)
except InvalidToken: except InvalidToken:
raise EncryptionFail("Password or token is incorrect") print("Password or Token Incorrect...")
# Probably the salt value got modified
except base64.binascii.Error:
print("Salt is invalid...")
# Some other kind of fuck up
except Exception as e: except Exception as e:
err = str(e) print("Unknown exception occurred...")
raise EncryptionFail("Decrypt Error: {}".format(err)) print(e)
# Exit if an exception was thrown
sys.exit(1)
# Glue functions that package **kwargs automatically
def settings_server_encrypt(settings_server):
kwargs = {"settings_server": settings_server}
return settings_server_new(_settings_server_encrypt, **kwargs)
def settings_server_decrypt(settings_server, settings_encrypt):
kwargs = {
"settings_server": settings_server,
"settings_encrypt": settings_encrypt
}
return settings_server_new(_settings_server_decrypt, **kwargs)
# The _cfg functions should return a regular string
# These are the functions that should interface with the bot a return a plain string
# settings_server ordered dictionary
def settings_server_encrypt_cfg(settings_server):
salt, settings_server = settings_server_encrypt(settings_server)
return salt_encode(salt), decode_dict(settings_server)
def settings_server_decrypt_cfg(settings_server, settings_encrypt):
settings_server = settings_server_decrypt(settings_server, settings_encrypt)
return decode_dict(settings_server)
def main(): def main():
@ -167,7 +202,6 @@ def main():
parser.add_argument("--encrypt", help="Generate encrypted authentication.", action="store_true") parser.add_argument("--encrypt", help="Generate encrypted authentication.", action="store_true")
parser.add_argument("--decrypt", help="Decrypt encrypted authentication", action="store_true") parser.add_argument("--decrypt", help="Decrypt encrypted authentication", action="store_true")
parser.add_argument("--recrypt", help="Recrypt encrypted authentication", action="store_true") parser.add_argument("--recrypt", help="Recrypt encrypted authentication", action="store_true")
parser.add_argument("-k", "--keyfile", help="Keyfile used for decryption", default=None)
parser.add_argument("-c", "--cfg", help="Specify config file.", default=default_cfg) parser.add_argument("-c", "--cfg", help="Specify config file.", default=default_cfg)
arguments = parser.parse_args() arguments = parser.parse_args()
@ -180,28 +214,25 @@ def main():
import importlib import importlib
cfg = importlib.import_module(arguments.cfg) cfg = importlib.import_module(arguments.cfg)
settings_server = cfg.settings_server settings_server = cfg.settings_server
settings_encrypt = cfg.settings_encrypt settings_encrypt = None
keyfile = arguments.keyfile or settings_encrypt["keyfile"]
if arguments.decrypt and arguments.encrypt: if arguments.decrypt and arguments.encrypt:
print("Re-encrypting") print("Re-encrypting")
if arguments.decrypt: # arguments.decrypt if arguments.decrypt: # arguments.decrypt
print("Decrypt...") print("Decrypt...")
settings_server = settings_server_decrypt(settings_server, settings_encrypt, arguments.keyfile) settings_server = settings_server_decrypt_cfg(cfg.settings_server, cfg.settings_encrypt)
settings_encrypt = OrderedDict([ settings_encrypt = OrderedDict([
("encrypt", False), ("encrypt", False),
("salt", settings_encrypt["salt"]), ("salt", cfg.settings_encrypt["encrypt"])
("keyfile", arguments.keyfile)
]) ])
if arguments.encrypt: if arguments.encrypt:
print("Encrypt...") print("Encrypt...")
salt, settings_server = settings_server_encrypt(settings_server, keyfile) salt, settings_server = settings_server_encrypt_cfg(settings_server)
settings_encrypt = OrderedDict([ settings_encrypt = OrderedDict([
("encrypt", True), ("encrypt", True),
("salt", salt), ("salt", salt)
("keyfile", arguments.keyfile)
]) ])
print("settings_server = {}".format(pformat(settings_server))) print("settings_server = {}".format(pformat(settings_server)))
@ -210,8 +241,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
try:
sys.exit(main()) sys.exit(main())
except EncryptionFail as e:
print(e)
sys.exit(1)