Guest User

Untitled

a guest
Mar 18th, 2025
13
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.92 KB | None | 0 0
  1. #!/usr/bin/env python3
  2.  
  3. import argparse
  4. import base64
  5. import os
  6. import sys
  7. import hashlib
  8. from nacl.signing import SigningKey, VerifyKey
  9. from nacl.bindings import crypto_scalarmult
  10. from Crypto.Cipher import AES
  11. from Crypto.Util import Counter
  12. from argon2.low_level import hash_secret_raw, Type
  13.  
  14. def generate_keys(private_key_file):
  15.     while True:
  16.         sk = SigningKey.generate()
  17.         vk = sk.verify_key
  18.         pub_bytes = vk.encode()
  19.         pub_b64 = base64.b64encode(pub_bytes).decode('ascii').rstrip("=")
  20.         if ("+" not in pub_b64) and ("/" not in pub_b64):
  21.             break
  22.  
  23.     priv_bytes = sk.encode()
  24.     if private_key_file == False:
  25.         private_key_file = pub_b64[:11].translate(str.maketrans("+/", "-_")) + ".priv"
  26.     with open(private_key_file, 'wb') as f:
  27.         f.write(priv_bytes)
  28.     print("Public key: ", end="")
  29.     print(pub_b64)
  30.     print(f"Private key saved to {private_key_file}")
  31.  
  32. def load_public_key(b64_pub):
  33.     missing_padding = len(b64_pub) % 4
  34.     if missing_padding:
  35.         b64_pub += '=' * (4 - missing_padding)
  36.     pub_bytes = base64.b64decode(b64_pub)
  37.     if len(pub_bytes) != 32:
  38.         raise ValueError("Invalid public key format")
  39.     return VerifyKey(pub_bytes)
  40.  
  41. def load_private_key_file(private_key_file):
  42.     with open(private_key_file, 'rb') as f:
  43.         priv_bytes = f.read()
  44.     return SigningKey(priv_bytes)
  45.  
  46. def derive_shared_key(priv_signing, peer_verify):
  47.     x25519_priv = priv_signing.to_curve25519_private_key()
  48.     x25519_peer = peer_verify.to_curve25519_public_key()
  49.     shared = crypto_scalarmult(x25519_priv.encode(), x25519_peer.encode())
  50.     h = hashlib.sha256(shared).digest()
  51.     return h[:16]
  52.  
  53. def argon2id_kdf(key_material, salt, hash_len=16):
  54.     return hash_secret_raw(secret=key_material, salt=salt, time_cost=2,
  55.                            memory_cost=1048576, parallelism=1, hash_len=hash_len, type=Type.ID)
  56.  
  57. def encrypt_file(recipient_b64, infile, outfile):
  58.     recipient_vk = load_public_key(recipient_b64)
  59.     eph_sk = SigningKey.generate()
  60.     eph_vk = eph_sk.verify_key
  61.     shared_key = derive_shared_key(eph_sk, recipient_vk)
  62.     salt = os.urandom(16)
  63.     final_key = argon2id_kdf(shared_key, salt, hash_len=16)
  64.     if infile:
  65.         with open(infile, 'rb') as f:
  66.             plaintext = f.read()
  67.     else:
  68.         plaintext = sys.stdin.buffer.read()
  69.     nonce = os.urandom(8)
  70.     ctr = Counter.new(64, prefix=nonce, initial_value=0)
  71.     cipher = AES.new(final_key, AES.MODE_CTR, counter=ctr)
  72.     ciphertext = cipher.encrypt(plaintext)
  73.     eph_pub_bytes = eph_vk.encode()
  74.     outdata = eph_pub_bytes + salt + nonce + ciphertext
  75.     outdata_b64 = base64.b64encode(outdata).decode('ascii').rstrip("=")
  76.     if outfile:
  77.         with open(outfile, 'w') as f:
  78.             f.write(outdata_b64)
  79.         print(f"Encryption complete. Output written to {outfile}")
  80.     else:
  81.         sys.stdout.write(outdata_b64)
  82.  
  83. def decrypt_file(private_key_file, infile, outfile):
  84.     sk = load_private_key_file(private_key_file)
  85.     vk = sk.verify_key
  86.     if infile:
  87.         with open(infile, 'r') as f:
  88.             b64_data = f.read()
  89.     else:
  90.         b64_data = sys.stdin.read()
  91.     missing_padding = len(b64_data) % 4
  92.     if missing_padding:
  93.         b64_data += '=' * (4 - missing_padding)
  94.     data = base64.b64decode(b64_data)
  95.     if len(data) < 32 + 16 + 8:
  96.         raise ValueError("Input file too short or corrupted")
  97.     eph_pub_bytes = data[:32]
  98.     eph_vk = VerifyKey(eph_pub_bytes)
  99.     salt = data[32:32+16]
  100.     nonce = data[32+16:32+16+8]
  101.     ciphertext = data[32+16+8:]
  102.     shared_key = derive_shared_key(sk, eph_vk)
  103.     final_key = argon2id_kdf(shared_key, salt, hash_len=16)
  104.     ctr = Counter.new(64, prefix=nonce, initial_value=0)
  105.     cipher = AES.new(final_key, AES.MODE_CTR, counter=ctr)
  106.     plaintext = cipher.decrypt(ciphertext)
  107.     if outfile:
  108.         with open(outfile, 'wb') as f:
  109.             f.write(plaintext)
  110.         print(f"Decryption complete. Output written to {outfile}")
  111.     else:
  112.         sys.stdout.buffer.write(plaintext)
  113.  
  114. def generate_public(private_key_file):
  115.     sk = load_private_key_file(private_key_file)
  116.     vk = sk.verify_key
  117.     pub_b64 = base64.b64encode(vk.encode()).decode('ascii').rstrip("=")
  118.     print("Exactracted public key: ", end="")
  119.     print(pub_b64)
  120.  
  121. def main():
  122.     parser = argparse.ArgumentParser(
  123.         description="Ed25519 key generation and ECIES-like encryption/decryption X25519 with Argon2id KDF."
  124.     )
  125.     group = parser.add_mutually_exclusive_group(required=True)
  126.     group.add_argument("-g", "--generate", action="store_true")
  127.     group.add_argument("-r", "--recipient", type=str)
  128.     group.add_argument("-k", "--private", type=str)
  129.     group.add_argument("-p", "--public", type=str)
  130.     parser.add_argument("-i", "--infile", type=str)
  131.     parser.add_argument("-o", "--outfile", type=str)
  132.     parser.add_argument("-f", "--force", action="store_true")
  133.     args = parser.parse_args()
  134.     if args.generate:
  135.         if not args.outfile:
  136.             args.outfile = False
  137.         if not args.force and args.outfile != False:
  138.             if os.path.exists(args.outfile):
  139.                 print("Output file exists. Use -f to overwrite", file=sys.stderr)
  140.                 sys.exit(1)
  141.         generate_keys(args.outfile)
  142.     elif args.recipient:
  143.         if not args.infile and sys.stdin.isatty():
  144.             print("Encryption mode requires input", file=sys.stderr)
  145.             sys.exit(1)
  146.         infile = args.infile if args.infile else None
  147.         encrypt_file(args.recipient, infile, args.outfile)
  148.     elif args.private:
  149.         if not args.infile and sys.stdin.isatty():
  150.             print("Decryption mode requires input", file=sys.stderr)
  151.             sys.exit(1)
  152.         infile = args.infile if args.infile else None
  153.         decrypt_file(args.private, infile, args.outfile)
  154.     elif args.public:
  155.         generate_public(args.public)
  156.  
  157. if __name__ == "__main__":
  158.     main()
  159.  
Add Comment
Please, Sign In to add comment