183 lines
5.5 KiB
Python
183 lines
5.5 KiB
Python
|
|
"""
|
||
|
|
CryptZ Ultimate v5 — CLI Module
|
||
|
|
Usage:
|
||
|
|
cryptz.py encrypt -p PASSWORD -o OUTPUT file1 file2 ...
|
||
|
|
cryptz.py decrypt -p PASSWORD -o OUTPUT_DIR archive.cryptz
|
||
|
|
cryptz.py verify -p PASSWORD archive.cryptz
|
||
|
|
"""
|
||
|
|
|
||
|
|
import argparse
|
||
|
|
import sys
|
||
|
|
import os
|
||
|
|
import getpass
|
||
|
|
|
||
|
|
# Force UTF-8 stdout on Windows
|
||
|
|
if sys.platform == "win32":
|
||
|
|
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
||
|
|
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
||
|
|
|
||
|
|
from crypto_engine import (
|
||
|
|
EncryptOptions, decrypt, encrypt, verify_integrity,
|
||
|
|
check_password_strength, EXTENSION,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def _progress(value: float, msg: str):
|
||
|
|
bar_len = 30
|
||
|
|
filled = int(bar_len * value)
|
||
|
|
bar = "#" * filled + "-" * (bar_len - filled)
|
||
|
|
line = " [{}] {:5.1f}% {:<40}".format(bar, value * 100, msg)
|
||
|
|
sys.stdout.write("\r" + line)
|
||
|
|
sys.stdout.flush()
|
||
|
|
if value >= 1.0:
|
||
|
|
print()
|
||
|
|
|
||
|
|
|
||
|
|
def cmd_encrypt(args):
|
||
|
|
password = args.password or getpass.getpass("Password: ")
|
||
|
|
score, label = check_password_strength(password)
|
||
|
|
if score < 2:
|
||
|
|
print("[!] Warning: password is {}. Consider strengthening it.".format(label.lower()))
|
||
|
|
|
||
|
|
if not args.files:
|
||
|
|
print("[ERROR] Specify files to encrypt.")
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
for f in args.files:
|
||
|
|
if not os.path.exists(f):
|
||
|
|
print("[ERROR] File not found: {}".format(f))
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
output = args.output or (os.path.splitext(args.files[0])[0] + EXTENSION)
|
||
|
|
|
||
|
|
options = EncryptOptions(
|
||
|
|
password=password,
|
||
|
|
files=args.files,
|
||
|
|
output_path=output,
|
||
|
|
use_2fa=args.twofa,
|
||
|
|
max_attempts=args.max_attempts,
|
||
|
|
hardware_key_path=args.key_file,
|
||
|
|
secure_delete=args.shred,
|
||
|
|
compress=not args.no_compress,
|
||
|
|
progress_callback=_progress,
|
||
|
|
)
|
||
|
|
|
||
|
|
if args.twofa:
|
||
|
|
import pyotp
|
||
|
|
options.otp_secret = pyotp.random_base32()
|
||
|
|
print("")
|
||
|
|
print("[2FA] Secret: {}".format(options.otp_secret))
|
||
|
|
uri = pyotp.TOTP(options.otp_secret).provisioning_uri("CryptZ", "CryptZ CLI")
|
||
|
|
print("[2FA] URI: {}".format(uri))
|
||
|
|
print("[2FA] Save this secret! It will be required for decryption.")
|
||
|
|
print("")
|
||
|
|
|
||
|
|
try:
|
||
|
|
result = encrypt(options)
|
||
|
|
print("")
|
||
|
|
print("[OK] Archive created: {}".format(result))
|
||
|
|
except Exception as e:
|
||
|
|
print("")
|
||
|
|
print("[ERROR] {}".format(e))
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
|
||
|
|
def cmd_decrypt(args):
|
||
|
|
password = args.password or getpass.getpass("Password: ")
|
||
|
|
|
||
|
|
if not os.path.exists(args.archive):
|
||
|
|
print("[ERROR] File not found: {}".format(args.archive))
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
output_dir = args.output or "."
|
||
|
|
os.makedirs(output_dir, exist_ok=True)
|
||
|
|
|
||
|
|
result = decrypt(
|
||
|
|
file_path=args.archive,
|
||
|
|
password=password,
|
||
|
|
output_dir=output_dir,
|
||
|
|
hardware_key_path=args.key_file,
|
||
|
|
otp_code=args.otp,
|
||
|
|
progress_callback=_progress,
|
||
|
|
)
|
||
|
|
|
||
|
|
if result.needs_2fa and not args.otp:
|
||
|
|
print("")
|
||
|
|
otp = input("[2FA] Enter TOTP code: ").strip()
|
||
|
|
result = decrypt(
|
||
|
|
file_path=args.archive,
|
||
|
|
password=password,
|
||
|
|
output_dir=output_dir,
|
||
|
|
hardware_key_path=args.key_file,
|
||
|
|
otp_code=otp,
|
||
|
|
progress_callback=_progress,
|
||
|
|
)
|
||
|
|
|
||
|
|
print("")
|
||
|
|
if result.success:
|
||
|
|
print("[OK] {}".format(result.message))
|
||
|
|
else:
|
||
|
|
print("[FAIL] {}".format(result.message))
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
|
||
|
|
def cmd_verify(args):
|
||
|
|
password = args.password or getpass.getpass("Password: ")
|
||
|
|
|
||
|
|
if not os.path.exists(args.archive):
|
||
|
|
print("[ERROR] File not found: {}".format(args.archive))
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
ok, msg = verify_integrity(args.archive, password, args.key_file)
|
||
|
|
if ok:
|
||
|
|
print("[OK] {}".format(msg))
|
||
|
|
else:
|
||
|
|
print("[FAIL] {}".format(msg))
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
|
||
|
|
def cli_main():
|
||
|
|
parser = argparse.ArgumentParser(
|
||
|
|
prog="cryptz",
|
||
|
|
description="CryptZ Ultimate v5 -- AES-256-GCM CLI Archiver",
|
||
|
|
)
|
||
|
|
sub = parser.add_subparsers(dest="command", required=True)
|
||
|
|
|
||
|
|
# encrypt
|
||
|
|
enc = sub.add_parser("encrypt", help="Encrypt files")
|
||
|
|
enc.add_argument("files", nargs="+", help="Files to encrypt")
|
||
|
|
enc.add_argument("-p", "--password", help="Password (or will be prompted)")
|
||
|
|
enc.add_argument("-o", "--output", help="Output file")
|
||
|
|
enc.add_argument("-k", "--key-file", help="Key file")
|
||
|
|
enc.add_argument("--2fa", dest="twofa", action="store_true", help="Enable 2FA")
|
||
|
|
enc.add_argument("--shred", action="store_true", help="Securely delete originals")
|
||
|
|
enc.add_argument("--no-compress", action="store_true", help="Disable compression")
|
||
|
|
enc.add_argument("--max-attempts", type=int, default=5, help="Max attempts (default: 5)")
|
||
|
|
|
||
|
|
# decrypt
|
||
|
|
dec = sub.add_parser("decrypt", help="Decrypt archive")
|
||
|
|
dec.add_argument("archive", help=".cryptz file")
|
||
|
|
dec.add_argument("-p", "--password", help="Password")
|
||
|
|
dec.add_argument("-o", "--output", help="Output directory")
|
||
|
|
dec.add_argument("-k", "--key-file", help="Key file")
|
||
|
|
dec.add_argument("--otp", help="2FA code")
|
||
|
|
|
||
|
|
# verify
|
||
|
|
ver = sub.add_parser("verify", help="Verify archive integrity")
|
||
|
|
ver.add_argument("archive", help=".cryptz file")
|
||
|
|
ver.add_argument("-p", "--password", help="Password")
|
||
|
|
ver.add_argument("-k", "--key-file", help="Key file")
|
||
|
|
|
||
|
|
args = parser.parse_args()
|
||
|
|
|
||
|
|
if args.command == "encrypt":
|
||
|
|
cmd_encrypt(args)
|
||
|
|
elif args.command == "decrypt":
|
||
|
|
cmd_decrypt(args)
|
||
|
|
elif args.command == "verify":
|
||
|
|
cmd_verify(args)
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
cli_main()
|