Nub-e

testamento_crypto_rsa

Nov 16th, 2025 (edited)
246
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.61 KB | Cybersecurity | 0 0
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. testamento_crypto_rsa.py
  5.  
  6. Uso:
  7.  pip install cryptography
  8.  python testamento_crypto_rsa.py initkeys
  9.  python testamento_crypto_rsa.py generar archivo.pdf
  10.  python testamento_crypto_rsa.py revelar
  11. """
  12.  
  13. import os, sys, secrets
  14. from pathlib import Path
  15. from cryptography.hazmat.primitives.ciphers.aead import AESGCM
  16. from cryptography.hazmat.primitives.asymmetric import rsa, padding
  17. from cryptography.hazmat.primitives import hashes, serialization
  18.  
  19. # Files
  20. KEY_FILE    = "key.bin"
  21. ENC_FILE    = "testamento.enc"
  22. NONCE_FILE  = "nonce.bin"
  23. SIG_FILE    = "signature.sig"
  24. SHARES_DIR  = "shares"
  25. PRIVATE_PEM = "private.pem"
  26. PUBLIC_PEM  = "public.pem"
  27.  
  28. # Use a known large prime > 2**256 (we can reuse the secp256k1 prime)
  29. PRIME = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
  30.  
  31. # ---------------- Shamir over prime field ----------------
  32.  
  33. def _int_from_bytes(b: bytes) -> int:
  34.     return int.from_bytes(b, "big")
  35.  
  36. def _int_to_bytes(i: int, length: int) -> bytes:
  37.     return i.to_bytes(length, "big")
  38.  
  39. def _eval_poly(coeffs, x, p):
  40.     """EvalĂșa polinomio con coeficientes (a0 + a1*x + a2*x^2 + ...) mod p"""
  41.     res = 0
  42.     pow_x = 1
  43.     for a in coeffs:
  44.         res = (res + a * pow_x) % p
  45.         pow_x = (pow_x * x) % p
  46.     return res
  47.  
  48. def shamir_split_prime(secret_bytes: bytes, n: int):
  49.     """Divide secret (bytes) en n shares (n-of-n) sobre PRIME"""
  50.     secret_int = _int_from_bytes(secret_bytes)
  51.     if secret_int >= PRIME:
  52.         raise ValueError("Secret too large for chosen PRIME")
  53.     # generar coeficientes aleatorios: a0 = secret, a1..a_{n-1} aleatorios mod PRIME
  54.     coeffs = [secret_int] + [secrets.randbelow(PRIME) for _ in range(n-1)]
  55.     shares = []
  56.     for x in range(1, n+1):
  57.         y = _eval_poly(coeffs, x, PRIME)
  58.         shares.append((x, y))
  59.     return shares
  60.  
  61. def _modinv(a, p):
  62.     """Inverso modular por pow (p prime)"""
  63.     return pow(a, p-2, p)
  64.  
  65. def shamir_recover_prime(shares):
  66.     """Recupera secreto dado lista de (x,y) sobre PRIME"""
  67.     k = len(shares)
  68.     # Lagrange interpolation at x=0 to get a0
  69.     secret = 0
  70.     for j, (xj, yj) in enumerate(shares):
  71.         num = 1
  72.         den = 1
  73.         for m, (xm, _) in enumerate(shares):
  74.             if m == j:
  75.                 continue
  76.             num = (num * (-xm)) % PRIME    # evaluate numerator at 0: product(-xm)
  77.             den = (den * (xj - xm)) % PRIME
  78.         lj = (num * _modinv(den % PRIME, PRIME)) % PRIME
  79.         secret = (PRIME + secret + (yj * lj)) % PRIME
  80.     return secret
  81.  
  82. # ---------------- RSA helpers ----------------
  83.  
  84. def initkeys():
  85.     if os.path.exists(PRIVATE_PEM) or os.path.exists(PUBLIC_PEM):
  86.         print("[!] private/public already exist. Remove them if you want to regenerate.")
  87.         return
  88.     print("[+] Generating RSA-2048 keys...")
  89.     key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
  90.     with open(PRIVATE_PEM, "wb") as f:
  91.         f.write(key.private_bytes(
  92.             serialization.Encoding.PEM,
  93.             serialization.PrivateFormat.PKCS8,
  94.             serialization.NoEncryption()
  95.         ))
  96.     with open(PUBLIC_PEM, "wb") as f:
  97.         f.write(key.public_key().public_bytes(
  98.             serialization.Encoding.PEM,
  99.             serialization.PublicFormat.SubjectPublicKeyInfo
  100.         ))
  101.     print("[+] Keys saved:", PRIVATE_PEM, PUBLIC_PEM)
  102.  
  103. def sign_file(filename):
  104.     with open(PRIVATE_PEM, "rb") as f:
  105.         priv = serialization.load_pem_private_key(f.read(), None)
  106.     with open(filename, "rb") as f:
  107.         data = f.read()
  108.     sig = priv.sign(
  109.         data,
  110.         padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
  111.         hashes.SHA256()
  112.     )
  113.     with open(SIG_FILE, "wb") as f:
  114.         f.write(sig)
  115.     print("[+] signature written:", SIG_FILE)
  116.  
  117. def verify_sig(filename):
  118.     with open(PUBLIC_PEM, "rb") as f:
  119.         pub = serialization.load_pem_public_key(f.read())
  120.     with open(filename, "rb") as f:
  121.         data = f.read()
  122.     with open(SIG_FILE, "rb") as f:
  123.         sig = f.read()
  124.     try:
  125.         pub.verify(
  126.             sig,
  127.             data,
  128.             padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
  129.             hashes.SHA256()
  130.         )
  131.         return True
  132.     except Exception as e:
  133.         print("[!] signature verify failed:", e)
  134.         return False
  135.  
  136. # ---------------- Main operations ----------------
  137.  
  138. def generar(testamento_path, n_shares=3):
  139.     if not os.path.exists(testamento_path):
  140.         print("[-] input not found:", testamento_path)
  141.         return
  142.     print("[+] Generating random AES-256 key...")
  143.     key = AESGCM.generate_key(bit_length=256)
  144.     key_len = len(key)
  145.     print(f"    key length: {key_len} bytes")
  146.     aes = AESGCM(key)
  147.     nonce = os.urandom(12)
  148.     with open(testamento_path, "rb") as f:
  149.         pt = f.read()
  150.     ct = aes.encrypt(nonce, pt, None)
  151.     # write artifacts
  152.     open(KEY_FILE, "wb").write(key)            # optional, remove later
  153.     open(NONCE_FILE, "wb").write(nonce)
  154.     open(ENC_FILE, "wb").write(ct)
  155.     print("[+] encryption done ->", ENC_FILE)
  156.     # require private key
  157.     if not os.path.exists(PRIVATE_PEM):
  158.         print("[!] private key missing. Run initkeys first.")
  159.         # cleanup
  160.         try:
  161.             os.remove(ENC_FILE)
  162.             os.remove(NONCE_FILE)
  163.             os.remove(KEY_FILE)
  164.         except Exception:
  165.             pass
  166.         return
  167.     print("[+] signing encrypted file...")
  168.     sign_file(ENC_FILE)
  169.     # Shamir split (prime-field)
  170.     print("[+] splitting key into shares (prime-field shamir)...")
  171.     shares = shamir_split_prime(key, n_shares)
  172.     os.makedirs(SHARES_DIR, exist_ok=True)
  173.     for x,y in shares:
  174.         # store as hex: "x-hex(y)"
  175.         path = os.path.join(SHARES_DIR, f"hijo{x}.share")
  176.         with open(path, "w") as f:
  177.             f.write(f"{x}-{y:x}")   # hex of y
  178.         print("   ->", path)
  179.     print("[+] done. Distribute shares to each heir.")
  180.     print("[!] For safety you may delete key.bin now.")
  181.  
  182. def revelar(n_shares=3):
  183.     # verify signature first
  184.     if not os.path.exists(ENC_FILE) or not os.path.exists(SIG_FILE) or not os.path.exists(NONCE_FILE):
  185.         print("[-] missing artifacts (enc/nonce/sig).")
  186.         return
  187.     if not os.path.exists(PUBLIC_PEM):
  188.         print("[-] public key missing; cannot verify signature.")
  189.         return
  190.     print("[+] verifying signature...")
  191.     if not verify_sig(ENC_FILE):
  192.         print("[-] signature invalid. abort.")
  193.         return
  194.     print("[+] signature OK.")
  195.     # read shares
  196.     shares = []
  197.     for i in range(1, n_shares+1):
  198.         path = os.path.join(SHARES_DIR, f"hijo{i}.share")
  199.         if not os.path.exists(path):
  200.             print("[-] missing share:", path)
  201.             return
  202.         s = open(path, "r").read().strip()
  203.         # format x-hex
  204.         if "-" not in s:
  205.             print("[-] bad share format in", path)
  206.             return
  207.         xs, yshex = s.split("-", 1)
  208.         x = int(xs)
  209.         y = int(yshex, 16)
  210.         shares.append((x,y))
  211.     print("[+] shares read.")
  212.     # recover secret int
  213.     secret_int = shamir_recover_prime(shares)
  214.     key_bytes = _int_to_bytes(secret_int, 32)  # AES-256 => 32 bytes
  215.     print("[DEBUG] reconstructed key hex:", key_bytes.hex())
  216.     print("[DEBUG] key length:", len(key_bytes))
  217.     # optional: compare with key.bin if exists
  218.     if os.path.exists(KEY_FILE):
  219.         actual = open(KEY_FILE, "rb").read()
  220.         print("[DEBUG] actual key.bin hex:", actual.hex())
  221.         print("[DEBUG] matches reconstructed:", actual == key_bytes)
  222.     # decrypt
  223.     aes = AESGCM(key_bytes)
  224.     nonce = open(NONCE_FILE, "rb").read()
  225.     ct = open(ENC_FILE, "rb").read()
  226.     try:
  227.         pt = aes.decrypt(nonce, ct, None)
  228.     except Exception as e:
  229.         print("[-] decryption failed:", e)
  230.         return
  231.     out = "testamento_revelado.pdf"
  232.     open(out, "wb").write(pt)
  233.     print("[+] decrypted ->", out)
  234.  
  235. # ---------------- CLI ----------------
  236.  
  237. def print_help():
  238.     me = Path(__file__).name
  239.     print("Usage:")
  240.     print(f"  python {me} initkeys")
  241.     print(f"  python {me} generar archivo.pdf")
  242.     print(f"  python {me} revelar")
  243.  
  244. def main():
  245.     if len(sys.argv) < 2:
  246.         print_help(); return
  247.     cmd = sys.argv[1].lower()
  248.     if cmd == "initkeys":
  249.         initkeys()
  250.     elif cmd == "generar":
  251.         if len(sys.argv) != 3:
  252.             print("specify file to encrypt"); return
  253.         generar(sys.argv[2])
  254.     elif cmd == "revelar":
  255.         revelar()
  256.     else:
  257.         print_help()
  258.  
  259. if __name__ == "__main__":
  260.     main()
  261.  
Advertisement
Add Comment
Please, Sign In to add comment