Files
PCS/PCS-A-3-i Terminal.py
2026-06-15 10:15:23 -04:00

380 lines
13 KiB
Python

import os
import getpass
import sys
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from pathlib import Path
import base64
# TODO Add option to write and read keyfile, where there is an option to store the salt!
if_verbose = False
def verbose(string):
if if_verbose: print(string)
# Encrypting data
def encrypt_data():
# Pesterish file asker
while True:
file_name = input("\nEnter the name of a zip file, without the extension that is in the same directory as this script. \nE.g. for encrypt.zip you would type \"encrypt\". Name > ") + ".zip"
if Path(file_name).is_file():
break
else:
print("This zip file does not exist!")
file_chosen_name = input("\nMake a name for your encrypted file, e.g. encrypted (press enter for default)\nName > ")
if file_chosen_name == "":
file_chosen_name = "encrypted"
keyfile = input("\nDo you want enable keyfile encryption?\nIf enabled, decryption is only possible with the file.[y]/[N] ")
if keyfile == "y" or keyfile == "Y":
print("Keyfile enabled.")
enable_keyfile = True
else:
print("Keyfile disabled.")
enable_keyfile = False
# Pestering password asker until you confirm it
while True:
password = getpass.getpass("\nCreating password > ")
confirm = input(f"Did you type the correct password, and remember it? [Y]/[n] ")
if confirm == "Y" or confirm == "y" or confirm == "":
password = password.encode()
break
# 1. Setup Salt
salt = os.urandom(16)
# 2. Derive Key
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=480000,
)
key = base64.urlsafe_b64encode(kdf.derive(password))
f = Fernet(key)
print("Encrypting...", end = " ")
try:
# 3. Encrypt and save SALT + DATA together
with open(file_name, 'rb') as file:
original_data = file.read()
encrypted_data = f.encrypt(original_data)
if not enable_keyfile:
with open(f"{file_chosen_name}.p7c_enc", 'wb') as file:
# Write the 16-byte salt first, then the encrypted data
file.write(salt)
file.write(encrypted_data)
elif enable_keyfile:
with open(f"key.p7c_key", 'wb') as key:
key.write(salt)
with open(f"{file_chosen_name}.p7c_enc", 'wb') as file:
file.write(encrypted_data)
else:
print("What the f** did you do something to enable_keyfile?")
print("Encrypted! ")
delete_original = input("Delete the original zip file for security? [Y]/[n] ")
if delete_original == "Y" or delete_original == "y" or delete_original == "":
os.remove(f"{file_name}")
print("Original file removed.")
except Exception as e:
print(f"Error! {e}")
# Encrypting data
def encrypt_data_neo(unencrypted_name, password, encrypted_name=None, use_key=None, keyfile_name=None, delete_original=None):
verbose(f"original file: {unencrypted_name}")
if not encrypted_name:
encrypted_name="encrypted"
file_chosen_name=encrypted_name
verbose(f"new file: {encrypted_name}")
if use_key:
verbose("Using key")
else:
verbose("not using key")
verbose(f"password: {password}")
password = password.encode()
verbose(f"password encoded: {password}")
salt = os.urandom(16)
verbose(f"salt: {salt}")
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=480000,
)
key = base64.urlsafe_b64encode(kdf.derive(password))
verbose(f"key: {key}")
f = Fernet(key)
verbose(f"f: {f}")
try:
with open(unencrypted_name, 'rb') as file:
original_data = file.read()
verbose(f"{unencrypted_name} has been read")
encrypted_data = f.encrypt(original_data)
verbose(f"data has been encrypted.")
if not use_key:
with open(f"{encrypted_name}", 'wb') as file:
# Write the 16-byte salt first, then the encrypted data
file.write(salt)
file.write(encrypted_data)
verbose(f"data was written to {encrypted_name} without key.")
elif use_key:
if not keyfile_name:
keyfile_name = "key.p7c_key"
verbose(f"keyfile_name: {keyfile_name}")
with open(f"{keyfile_name}", 'wb') as key:
key.write(salt)
verbose("salt has been written.")
with open(f"{encrypted_name}", 'wb') as file:
file.write(encrypted_data)
verbose("encrypted data has been written.")
else:
verbose("🔥🔥🔥 THIS IS AN ERROR 🔥🔥🔥")
print(f"This is P7MJ. I am speechless. use_key: {use_key}")
if delete_original:
os.remove(f"{unencrypted_name}")
verbose("Original file has been removed.")
except Exception as e:
verbose("🔥🔥🔥 THIS IS AN ERROR 🔥🔥🔥")
print(f"\nAn error has occured.\n {e}\n")
# Decrypting data
def decrypt_data():
# 1. Get the password from the user
while True:
choose_file = input("\nName of the p7c_enc file without extension, e.g. \"encrypted\"\nName > ") + ".p7c_enc"
if Path(choose_file).is_file():
break
else:
print("This p7c_enc file does not exist!")
key_exists = input("Do you have a p7c_key keyfile? [y]/[N] ")
if key_exists == "y" or key_exists == "Y":
while True:
input("Double check if your key is in the same direcory as this code and your encrypted file.\nIt must be named key.p7c_key. Press ENTER to check.")
if Path("key.p7c_key").is_file():
break
else:
print("Does not exist!")
there_is_a_key = True
else:
there_is_a_key = False
password = getpass.getpass("Password > ").encode()
# 2. Open the encrypted file and extract the salt + data
if there_is_a_key:
with open("key.p7c_key", 'rb') as file:
file_salt = file.read(16)
with open(choose_file, 'rb') as file:
encrypted_data = file.read()
elif not there_is_a_key:
with open(choose_file, 'rb') as file:
# Read exactly 16 bytes for the salt
file_salt = file.read(16)
# Read everything else as the encrypted data
encrypted_data = file.read()
else:
print("WHAT DID YOU DO TO THERE_IS_A_KEY?!")
# 3. Re-derive the EXACT same key using that salt
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=file_salt,
iterations=480000,
)
key = base64.urlsafe_b64encode(kdf.derive(password))
f = Fernet(key)
# 4. Decrypt and save the original file
print("Decrypting...", end = " ")
try:
decrypted_data = f.decrypt(encrypted_data)
with open('unencrypted.zip', 'wb') as file:
file.write(decrypted_data)
print("Success.")
delete_encrypted = input("\nDelete the encrypted file (and key, if exists)? [Y]/[n] ")
if delete_encrypted == "Y" or delete_encrypted == "y" or delete_encrypted == "":
os.remove(choose_file)
if there_is_a_key:
os.remove("key.p7c_key")
print("Key removed.")
print("Encrypted file removed.")
except Exception as e:
print(f"Could not decrypt!.\nThis might be due to a wrong file or corrupted data?\nDetailed info:\n{e}.")
# Decrypting data
def decrypt_data_neo(encrypted_name, password, unencrypted_name=None, use_key=None, keyfile_name=None, delete_original=None):
choose_file = encrypted_name
if not unencrypted_name:
unencrypted_name = "unencrypted"
password = password.encode()
if use_key and not keyfile_name:
keyfile_name = "key.p7c_key"
# 2. Open the encrypted file and extract the salt + data
if use_key:
with open(f"{keyfile_name}", 'rb') as file:
file_salt = file.read(16)
with open(choose_file, 'rb') as file:
encrypted_data = file.read()
elif not use_key:
with open(encrypted_name, 'rb') as file:
# Read exactly 16 bytes for the salt
file_salt = file.read(16)
# Read everything else as the encrypted data
encrypted_data = file.read()
else:
verbose("🔥🔥🔥 THIS IS AN ERROR 🔥🔥🔥 flames are burning and so is my brain")
print("WHAT DID YOU DO TO THERE_IS_A_KEY?!")
# 3. Re-derive the EXACT same key using that salt
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=file_salt,
iterations=480000,
)
key = base64.urlsafe_b64encode(kdf.derive(password))
verbose(f"key: {key}")
f = Fernet(key)
verbose(f"f: {f}")
try:
decrypted_data = f.decrypt(encrypted_data)
verbose("data unencrypted.")
with open(f'{unencrypted_name}', 'wb') as file:
file.write(decrypted_data)
verbose("unencrypted data written to disk.")
if delete_original:
os.remove(encrypted_name)
verbose("Encrypted file removed.")
if use_key:
os.remove(keyfile_name)
verbose("Key removed.")
except Exception as e:
print(f"\nError. Could not decrypt. It is likely that your password is wrong.\nError: {e}.\n")
# Main function
def old_main():
while True:
print("\nPCS - P7MJ's enCryption System | V A-2-i Dividend | P7MJ")
choice = input("[1] Encrypt [2] Decrypt [x] Exit > ")
if choice == "1":
encrypt_data()
elif choice == "2":
decrypt_data()
elif choice == "x":
sys.exit(0)
else:
print("Not an option!!!")
if __name__ == "__main__":
try:
while True:
inputs = input("[PCS] ")
list_inputs = inputs.split()
if not list_inputs:
continue
for i, param in enumerate(list_inputs):
if param == "None" or param == "0" or param == "No" or param == "no" or param == "False":
list_inputs[i] = None
if param == "True" or param == "1" or param == "Yes" or param == "yes" or param == "Exists" or param == "exists":
list_inputs[i] = True
if list_inputs[0] == "help":
print("encrypt unencrypted_name encrypted_name=None password use_key=None keyfile_name=None delete_original=None")
# 1 2 3 4 5 6
print("decrypt encrypted_name unencrypted_name=None password use_key=None keyfile_name=None delete_original=None")
# 1 2 3 4 5 6
print("\nDue to this program being able to encrypt absolutely ANYTHING, It is recommended to name your encrypted archives after your original file.")
print("E.g. archive.zip should be named archive.zip.p7c_enc.")
print("This ensures that you remember the extension.")
print("\nFor compatibility with PCS A-2-i Dividend, the keyfile must be named key.p7c_key. Additionally, append .p7c_enc to all encrypted archive names.")
print("For compatability with PCS A-1-i Proto, follow A-2-i standards, but do not use any form of keyfile.")
elif list_inputs[0] == "credits":
print("PCS A-3-i Terminal by P7MJ.")
elif list_inputs[0] == "exit":
sys.exit(0)
elif list_inputs[0] == "encrypt":
if len(list_inputs) == 7:
# unencrypted_name, password, encrypted_name=None, use_key=None, keyfile_name=None, delete_original=None
encrypt_data_neo(list_inputs[1], list_inputs[3], encrypted_name=list_inputs[2], use_key=list_inputs[4], keyfile_name=list_inputs[5], delete_original=list_inputs[6])
else:
print("Invalid use of encrypt: length of parameters incorrect.")
print('If you wish to leave a parameter empty, type "None", "0", "No", "no", or "False".')
print('Type "help" for more information.')
elif list_inputs[0] == "decrypt":
if len(list_inputs) == 7:
# encrypted_name, password, unencrypted_name=None, use_key=None, keyfile_name=None, delete_original=None
decrypt_data_neo(list_inputs[1], list_inputs[3], unencrypted_name=list_inputs[2], use_key=list_inputs[4], keyfile_name=list_inputs[5], delete_original=list_inputs[6])
else:
print("Invalid use of decrypt: length of parameters incorrect.")
print('If you wish to leave a parameter empty, type "None", "0", "No", "no", or "False".')
print('Type "help" for more information.')
except KeyboardInterrupt:
pass
except Exception as e:
print(f"{e}")