Updated to A-3-i

This commit is contained in:
2026-06-15 10:15:23 -04:00
parent 53683d5d85
commit 01733df39e
4 changed files with 414 additions and 42 deletions

379
PCS-A-3-i Terminal.py Normal file
View File

@@ -0,0 +1,379 @@
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}")

View File

@@ -1,60 +1,52 @@
# PCS - P7MJ's enCryption System # PCS - P7MJ's enCryption System A-3-i "Terminal"
**Version:** A-1-i Proto
**Author:** P7MJ
PCS is a lightweight, secure Python utility designed to encrypt and decrypt ZIP files (and other binary data) using industry-standard AES-256 encryption via the Fernet protocol. ## 🔤 Introduction
## 🚀 Features PCS is a secure and lightweight Python utility designed to encrypt and decrypt any type of file that can be read in binary. Virtually every file can be encrypted and decrypted, including archives, pictures, movies, code, and programs.
* **Hardened Key Derivation:** Uses `PBKDF2HMAC` with **480,000 iterations** to turn your password into a cryptographically strong key.
* **Salted Encryption:** Generates a unique 16-byte random salt for every encryption process, protecting against rainbow table attacks.
* **Embedded Metadata:** The salt is stored directly inside the encrypted file (`.p7c_enc`), so you only need your password to decrypt.
* **Authenticated Encryption:** Uses Fernet, which ensures that if the file is tampered with or the password is wrong, the system will refuse to decrypt rather than producing corrupted data.
* **User Safety:** Includes file existence verification and optional "shredding" (deletion) of source files after processing.
--- It is the standard encryption program for [BUGPy-mOS](https://git.wholeworldcoding.com/wholeworldcoding/BUGPy-mOS) (implemented as the FDEA launcher and encryption "ant"). An implementation derived from the older A-2-i is included as part of the core script.
## 🛠️ Installation This version features total extension freedom. You can now not only encrypt `.zip` files, but any type of file readable in binary. Additionally, there are no longer any limits regarding the names and extensions of the archives and the keyfiles generated.
A-3-i, is compatible both ways with A-2-i, provided that you follow A-2-i's strict filename extension protocols. A-3-i and A-2-i are compatible with A-1-i if you do not use keyfiles while encrypting.
PCS is tested on **Debian Trixie** only. It should work on other Linux distributions, Windows, and Mac, but it is not tested.
## 🧩 Features
- Uses `PBKDF2HMAC` with **480,000 iterations** to make your password strong, cryptographically.
- Generates a unique 16-byte random salt for every encryption process, protecting against rainbow table attacks.
- Salts can be stored directly inside the encrypted file, or outside the file as a key, providing versatility and security.
- Fernet ensures that if the file is corrupted, tampered with, or an attempt to decrypt it has a wrong password, the program will refuse to decrypt rather than producing corrupt data.
- Shredding of original files, including keyfiles, is supported after encryption or decryption.
> [!TIP]
> The security of the archive depends on your password! Since there is no limit to your password length, or complexity, ensure your password has >8 chars and includes symbols and numbers.
## 🛠️ Installation and Dependencies
PCS requires Python 3.x and the `cryptography` library. PCS requires Python 3.x and the `cryptography` library.
1. **Clone or download** this repository. 1. **Clone this repository** or download a release from the [Releases](https://git.wholeworldcoding.com/p7mj/PCS/releases) page.
2. **Install the dependency:** 2. **Install the dependency:**
```bash ```bash
pip install cryptography pip install cryptography
``` ```
--- ## 🧑‍💻 Usage
## 📖 How to Use Navigate to the folder and run:
Run the script using: ```
```bash python3 PCS-A-3-i\ Terminal.py
python PCS-A-1-i-Proto.py
``` ```
### 1. Encrypting a File After you are inside, type `help` for built-in help.
* Select option `[1]`.
* Enter the name of your `.zip` file (the script automatically appends the extension).
* Create a password. **Important:** If you lose this password, your data is unrecoverable.
* The script creates a file with the `.p7c_enc` extension.
* Choose whether to "shred" (permanently delete) the original file.
### 2. Decrypting a File ## 🏅 Credits
* Select option `[2]`.
* Enter the full name of the `.p7c_enc` file.
* Enter your password.
* The file will be restored as `unencrypted.zip`.
--- P7MJ Original.
## ⚠️ Important Security Notes
* **Password Length:** While the script is technically secure, the strength of the encryption relies entirely on the complexity of your password.
* **The Salt:** The first 16 bytes of your `.p7c_enc` file is the salt. Do not modify or "trim" the encrypted file, or it will become undecryptable.
* **Permanent Deletion:** The "shred" feature uses `os.remove()`, which bypasses the Recycle Bin/Trash on most systems. Use with caution!
---
## 🏗️ Technical Details
* **Encryption Engine:** AES-128 in CBC mode with PKCS7 padding.
* **Authentication:** HMAC using SHA256.
* **KDF:** PBKDF2 with SHA256.

BIN
archive/BUGPY-mOS.zip Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long