mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-08-14 04:13:45 +02:00
Release v0.7.1 - Remote Manifest System, Stability Fixes
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
"""Machine-tied AES-GCM encrypt/decrypt helpers.
|
||||
|
||||
Key derivation: sha256(hostname:username:machine-id:jackify) -> urlsafe_b64encode.
|
||||
decrypt() returns None on ANY failure - never returns garbage or plaintext leakage.
|
||||
"""
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_machine_key() -> bytes:
|
||||
"""Return the machine-tied 32-byte AES key (base64-encoded for AES-GCM use)."""
|
||||
import socket
|
||||
import getpass
|
||||
try:
|
||||
hostname = socket.gethostname()
|
||||
username = getpass.getuser()
|
||||
machine_id = None
|
||||
for id_path in ('/etc/machine-id', '/var/lib/dbus/machine-id'):
|
||||
try:
|
||||
with open(id_path, 'r') as f:
|
||||
machine_id = f.read().strip()
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
key_material = (
|
||||
f"{hostname}:{username}:{machine_id}:jackify"
|
||||
if machine_id
|
||||
else f"{hostname}:{username}:jackify"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning("Failed to get machine info for encryption key: %s", e)
|
||||
key_material = "jackify:default:key"
|
||||
return base64.urlsafe_b64encode(hashlib.sha256(key_material.encode('utf-8')).digest())
|
||||
|
||||
|
||||
def encrypt(plaintext: str) -> str:
|
||||
"""Encrypt plaintext with AES-GCM. Returns "" if pycryptodomex is unavailable."""
|
||||
try:
|
||||
from Cryptodome.Cipher import AES
|
||||
from Cryptodome.Random import get_random_bytes
|
||||
key = base64.urlsafe_b64decode(get_machine_key())
|
||||
nonce = get_random_bytes(12)
|
||||
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
|
||||
ciphertext, tag = cipher.encrypt_and_digest(plaintext.encode('utf-8'))
|
||||
combined = nonce + ciphertext + tag
|
||||
return base64.b64encode(combined).decode('utf-8')
|
||||
except ImportError:
|
||||
logger.warning("pycryptodomex not available - encryption disabled")
|
||||
return ""
|
||||
except Exception as e:
|
||||
logger.error("Encryption failed: %s", e)
|
||||
return ""
|
||||
|
||||
|
||||
def decrypt(ciphertext: str) -> Optional[str]:
|
||||
"""Decrypt ciphertext produced by encrypt(). Returns None on ANY failure."""
|
||||
try:
|
||||
from Cryptodome.Cipher import AES
|
||||
key = base64.urlsafe_b64decode(get_machine_key())
|
||||
combined = base64.b64decode(ciphertext.encode('utf-8'))
|
||||
if len(combined) < 28:
|
||||
return None
|
||||
nonce = combined[:12]
|
||||
tag = combined[-16:]
|
||||
data = combined[12:-16]
|
||||
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
|
||||
plaintext = cipher.decrypt_and_verify(data, tag)
|
||||
return plaintext.decode('utf-8')
|
||||
except ImportError:
|
||||
logger.warning("pycryptodomex not available - cannot decrypt")
|
||||
return None
|
||||
except Exception:
|
||||
return None
|
||||
Reference in New Issue
Block a user