Guest User

Untitled

a guest
Mar 18th, 2025
74
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/env python3
  2.  
  3. import argparse
  4. import base64
  5. import os
  6. import sys
  7. import hashlib
  8. from ecdsa import SigningKey, VerifyingKey, SECP112r1
  9. from Crypto.Cipher import AES
  10. from Crypto.Util import Counter
  11. from argon2.low_level import hash_secret_raw, Type
  12.  
  13. def generate_keys(private_key_file):
  14.     sk = SigningKey.generate(curve=SECP112r1)
  15.     vk = sk.get_verifying_key()
  16.     priv_bytes = sk.to_string()
  17.     pub_bytes = b'\x04' + vk.to_string()
  18.     pub_b32 = base64.b32encode(pub_bytes).decode('ascii').lower().rstrip('=')
  19.     if private_key_file == False:
  20.         private_key_file = pub_b32[:16] + ".key"
  21.     with open(private_key_file, 'wb') as f:
  22.         f.write(priv_bytes)
  23.     print("Public key: ", end="")
  24.     print(pub_b32)
  25.     print(f"Private key saved to {private_key_file}")
  26.  
  27. def load_public_key(b32_pub):
  28.     pad = '=' * ((8 - len(b32_pub) % 8) % 8)
  29.     pub_bytes = base64.b32decode(b32_pub.upper() + pad)
  30.     if pub_bytes[0] != 0x04:
  31.         raise ValueError("Invalid public key format")
  32.     return VerifyingKey.from_string(pub_bytes[1:], curve=SECP112r1)
  33.  
  34. def load_private_key_file(private_key_file):
  35.     with open(private_key_file, 'rb') as f:
  36.         priv_bytes = f.read()
  37.     return SigningKey.from_string(priv_bytes, curve=SECP112r1)
  38.  
  39. def derive_shared_key(priv_scalar, other_pub_point, key_bytes=16):
  40.     shared_point = other_pub_point * priv_scalar
  41.     shared_x = shared_point.x()
  42.     shared_x_bytes = shared_x.to_bytes(14, byteorder='big')
  43.     h = hashlib.sha256(shared_x_bytes).digest()
  44.     return h[:key_bytes]
  45.  
  46. def argon2id_kdf(key_material, salt, hash_len=16):
  47.     return hash_secret_raw(
  48.         secret=key_material,
  49.         salt=salt,
  50.         time_cost=2,
  51.         memory_cost=1048576,
  52.         parallelism=1,
  53.         hash_len=hash_len,
  54.         type=Type.ID
  55.     )
  56.  
  57. def encrypt_file(recipient_b32, infile, outfile):
  58.     recipient_vk = load_public_key(recipient_b32)
  59.     eph_sk = SigningKey.generate(curve=SECP112r1)
  60.     eph_vk = eph_sk.get_verifying_key()
  61.     shared_key = derive_shared_key(eph_sk.privkey.secret_multiplier, recipient_vk.pubkey.point)
  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 = b'\x04' + eph_vk.to_string()
  74.     outdata = eph_pub_bytes + salt + nonce + ciphertext
  75.     if outfile:
  76.         with open(outfile, 'wb') as f:
  77.             f.write(outdata)
  78.         print(f"Encryption complete. Output written to {outfile}")
  79.     else:
  80.         sys.stdout.buffer.write(outdata)
  81.  
  82. def decrypt_file(private_key_file, infile, outfile):
  83.     sk = load_private_key_file(private_key_file)
  84.     if infile:
  85.         data = open(infile, 'rb').read()
  86.     else:
  87.         data = sys.stdin.buffer.read()
  88.     if len(data) < 53:
  89.         raise ValueError("Input file too short or corrupted")
  90.     eph_pub_bytes = data[:29]
  91.     if eph_pub_bytes[0] != 0x04:
  92.         raise ValueError("Invalid ephemeral public key format")
  93.     eph_pub = VerifyingKey.from_string(eph_pub_bytes[1:], curve=SECP112r1)
  94.     salt = data[29:29+16]
  95.     nonce = data[29+16:29+16+8]
  96.     ciphertext = data[29+16+8:]
  97.     shared_key = derive_shared_key(sk.privkey.secret_multiplier, eph_pub.pubkey.point)
  98.     final_key = argon2id_kdf(shared_key, salt, hash_len=16)
  99.     ctr = Counter.new(64, prefix=nonce, initial_value=0)
  100.     cipher = AES.new(final_key, AES.MODE_CTR, counter=ctr)
  101.     plaintext = cipher.decrypt(ciphertext)
  102.     if outfile:
  103.         with open(outfile, 'wb') as f:
  104.             f.write(plaintext)
  105.         print(f"Decryption complete. Output written to {outfile}")
  106.     else:
  107.         sys.stdout.buffer.write(plaintext)
  108.  
  109. def generate_public(private_key_file):
  110.     sk = load_private_key_file(private_key_file)
  111.     vk = sk.get_verifying_key()
  112.     pub_bytes = b'\x04' + vk.to_string()
  113.     pub_b32 = base64.b32encode(pub_bytes).decode('ascii').lower().rstrip('=')
  114.     print("Public key: ", end="")
  115.     print(pub_b32)
  116.  
  117. def main():
  118.     parser = argparse.ArgumentParser(description="ECC Key Generation and ECIES-like Encryption/Decryption using secp112r1 with Argon2id KDF")
  119.     group = parser.add_mutually_exclusive_group(required=True)
  120.     group.add_argument("-g", "--generate", action="store_true")
  121.     group.add_argument("-r", "--recipient", type=str)
  122.     group.add_argument("-k", "--private", type=str)
  123.     group.add_argument("-p", "--public", type=str)
  124.     parser.add_argument("-i", "--infile", type=str)
  125.     parser.add_argument("-o", "--outfile", type=str)
  126.     parser.add_argument("-f", "--force", action="store_true")
  127.     args = parser.parse_args()
  128.     if args.generate:
  129.         if not args.outfile:
  130.             args.outfile = False
  131.         if not args.force:
  132.             if args.outfile != False:
  133.                 if os.path.exists(args.outfile):
  134.                     print("Output file exists. Use -f to overwrite", file=sys.stderr)
  135.                     sys.exit(1)
  136.         generate_keys(args.outfile)
  137.     elif args.recipient:
  138.         if not args.infile and sys.stdin.isatty():
  139.             print("Encryption mode requires input", file=sys.stderr)
  140.             sys.exit(1)
  141.         infile = args.infile if args.infile else None
  142.         encrypt_file(args.recipient, infile, args.outfile)
  143.     elif args.private:
  144.         if not args.infile and sys.stdin.isatty():
  145.             print("Decryption mode requires input", file=sys.stderr)
  146.             sys.exit(1)
  147.         infile = args.infile if args.infile else None
  148.         decrypt_file(args.private, infile, args.outfile)
  149.     elif args.public:
  150.         generate_public(args.public)
  151.  
  152. if __name__ == "__main__":
  153.     main()
  154.  
Add Comment
Please, Sign In to add comment