Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- """
- testamento_crypto_rsa.py
- Uso:
- pip install cryptography
- python testamento_crypto_rsa.py initkeys
- python testamento_crypto_rsa.py generar archivo.pdf
- python testamento_crypto_rsa.py revelar
- """
- import os, sys, secrets
- from pathlib import Path
- from cryptography.hazmat.primitives.ciphers.aead import AESGCM
- from cryptography.hazmat.primitives.asymmetric import rsa, padding
- from cryptography.hazmat.primitives import hashes, serialization
- # Files
- KEY_FILE = "key.bin"
- ENC_FILE = "testamento.enc"
- NONCE_FILE = "nonce.bin"
- SIG_FILE = "signature.sig"
- SHARES_DIR = "shares"
- PRIVATE_PEM = "private.pem"
- PUBLIC_PEM = "public.pem"
- # Use a known large prime > 2**256 (we can reuse the secp256k1 prime)
- PRIME = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
- # ---------------- Shamir over prime field ----------------
- def _int_from_bytes(b: bytes) -> int:
- return int.from_bytes(b, "big")
- def _int_to_bytes(i: int, length: int) -> bytes:
- return i.to_bytes(length, "big")
- def _eval_poly(coeffs, x, p):
- """EvalĂșa polinomio con coeficientes (a0 + a1*x + a2*x^2 + ...) mod p"""
- res = 0
- pow_x = 1
- for a in coeffs:
- res = (res + a * pow_x) % p
- pow_x = (pow_x * x) % p
- return res
- def shamir_split_prime(secret_bytes: bytes, n: int):
- """Divide secret (bytes) en n shares (n-of-n) sobre PRIME"""
- secret_int = _int_from_bytes(secret_bytes)
- if secret_int >= PRIME:
- raise ValueError("Secret too large for chosen PRIME")
- # generar coeficientes aleatorios: a0 = secret, a1..a_{n-1} aleatorios mod PRIME
- coeffs = [secret_int] + [secrets.randbelow(PRIME) for _ in range(n-1)]
- shares = []
- for x in range(1, n+1):
- y = _eval_poly(coeffs, x, PRIME)
- shares.append((x, y))
- return shares
- def _modinv(a, p):
- """Inverso modular por pow (p prime)"""
- return pow(a, p-2, p)
- def shamir_recover_prime(shares):
- """Recupera secreto dado lista de (x,y) sobre PRIME"""
- k = len(shares)
- # Lagrange interpolation at x=0 to get a0
- secret = 0
- for j, (xj, yj) in enumerate(shares):
- num = 1
- den = 1
- for m, (xm, _) in enumerate(shares):
- if m == j:
- continue
- num = (num * (-xm)) % PRIME # evaluate numerator at 0: product(-xm)
- den = (den * (xj - xm)) % PRIME
- lj = (num * _modinv(den % PRIME, PRIME)) % PRIME
- secret = (PRIME + secret + (yj * lj)) % PRIME
- return secret
- # ---------------- RSA helpers ----------------
- def initkeys():
- if os.path.exists(PRIVATE_PEM) or os.path.exists(PUBLIC_PEM):
- print("[!] private/public already exist. Remove them if you want to regenerate.")
- return
- print("[+] Generating RSA-2048 keys...")
- key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
- with open(PRIVATE_PEM, "wb") as f:
- f.write(key.private_bytes(
- serialization.Encoding.PEM,
- serialization.PrivateFormat.PKCS8,
- serialization.NoEncryption()
- ))
- with open(PUBLIC_PEM, "wb") as f:
- f.write(key.public_key().public_bytes(
- serialization.Encoding.PEM,
- serialization.PublicFormat.SubjectPublicKeyInfo
- ))
- print("[+] Keys saved:", PRIVATE_PEM, PUBLIC_PEM)
- def sign_file(filename):
- with open(PRIVATE_PEM, "rb") as f:
- priv = serialization.load_pem_private_key(f.read(), None)
- with open(filename, "rb") as f:
- data = f.read()
- sig = priv.sign(
- data,
- padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
- hashes.SHA256()
- )
- with open(SIG_FILE, "wb") as f:
- f.write(sig)
- print("[+] signature written:", SIG_FILE)
- def verify_sig(filename):
- with open(PUBLIC_PEM, "rb") as f:
- pub = serialization.load_pem_public_key(f.read())
- with open(filename, "rb") as f:
- data = f.read()
- with open(SIG_FILE, "rb") as f:
- sig = f.read()
- try:
- pub.verify(
- sig,
- data,
- padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
- hashes.SHA256()
- )
- return True
- except Exception as e:
- print("[!] signature verify failed:", e)
- return False
- # ---------------- Main operations ----------------
- def generar(testamento_path, n_shares=3):
- if not os.path.exists(testamento_path):
- print("[-] input not found:", testamento_path)
- return
- print("[+] Generating random AES-256 key...")
- key = AESGCM.generate_key(bit_length=256)
- key_len = len(key)
- print(f" key length: {key_len} bytes")
- aes = AESGCM(key)
- nonce = os.urandom(12)
- with open(testamento_path, "rb") as f:
- pt = f.read()
- ct = aes.encrypt(nonce, pt, None)
- # write artifacts
- open(KEY_FILE, "wb").write(key) # optional, remove later
- open(NONCE_FILE, "wb").write(nonce)
- open(ENC_FILE, "wb").write(ct)
- print("[+] encryption done ->", ENC_FILE)
- # require private key
- if not os.path.exists(PRIVATE_PEM):
- print("[!] private key missing. Run initkeys first.")
- # cleanup
- try:
- os.remove(ENC_FILE)
- os.remove(NONCE_FILE)
- os.remove(KEY_FILE)
- except Exception:
- pass
- return
- print("[+] signing encrypted file...")
- sign_file(ENC_FILE)
- # Shamir split (prime-field)
- print("[+] splitting key into shares (prime-field shamir)...")
- shares = shamir_split_prime(key, n_shares)
- os.makedirs(SHARES_DIR, exist_ok=True)
- for x,y in shares:
- # store as hex: "x-hex(y)"
- path = os.path.join(SHARES_DIR, f"hijo{x}.share")
- with open(path, "w") as f:
- f.write(f"{x}-{y:x}") # hex of y
- print(" ->", path)
- print("[+] done. Distribute shares to each heir.")
- print("[!] For safety you may delete key.bin now.")
- def revelar(n_shares=3):
- # verify signature first
- if not os.path.exists(ENC_FILE) or not os.path.exists(SIG_FILE) or not os.path.exists(NONCE_FILE):
- print("[-] missing artifacts (enc/nonce/sig).")
- return
- if not os.path.exists(PUBLIC_PEM):
- print("[-] public key missing; cannot verify signature.")
- return
- print("[+] verifying signature...")
- if not verify_sig(ENC_FILE):
- print("[-] signature invalid. abort.")
- return
- print("[+] signature OK.")
- # read shares
- shares = []
- for i in range(1, n_shares+1):
- path = os.path.join(SHARES_DIR, f"hijo{i}.share")
- if not os.path.exists(path):
- print("[-] missing share:", path)
- return
- s = open(path, "r").read().strip()
- # format x-hex
- if "-" not in s:
- print("[-] bad share format in", path)
- return
- xs, yshex = s.split("-", 1)
- x = int(xs)
- y = int(yshex, 16)
- shares.append((x,y))
- print("[+] shares read.")
- # recover secret int
- secret_int = shamir_recover_prime(shares)
- key_bytes = _int_to_bytes(secret_int, 32) # AES-256 => 32 bytes
- print("[DEBUG] reconstructed key hex:", key_bytes.hex())
- print("[DEBUG] key length:", len(key_bytes))
- # optional: compare with key.bin if exists
- if os.path.exists(KEY_FILE):
- actual = open(KEY_FILE, "rb").read()
- print("[DEBUG] actual key.bin hex:", actual.hex())
- print("[DEBUG] matches reconstructed:", actual == key_bytes)
- # decrypt
- aes = AESGCM(key_bytes)
- nonce = open(NONCE_FILE, "rb").read()
- ct = open(ENC_FILE, "rb").read()
- try:
- pt = aes.decrypt(nonce, ct, None)
- except Exception as e:
- print("[-] decryption failed:", e)
- return
- out = "testamento_revelado.pdf"
- open(out, "wb").write(pt)
- print("[+] decrypted ->", out)
- # ---------------- CLI ----------------
- def print_help():
- me = Path(__file__).name
- print("Usage:")
- print(f" python {me} initkeys")
- print(f" python {me} generar archivo.pdf")
- print(f" python {me} revelar")
- def main():
- if len(sys.argv) < 2:
- print_help(); return
- cmd = sys.argv[1].lower()
- if cmd == "initkeys":
- initkeys()
- elif cmd == "generar":
- if len(sys.argv) != 3:
- print("specify file to encrypt"); return
- generar(sys.argv[2])
- elif cmd == "revelar":
- revelar()
- else:
- print_help()
- if __name__ == "__main__":
- main()
Advertisement
Add Comment
Please, Sign In to add comment