58 lines
No EOL
1.8 KiB
Python
58 lines
No EOL
1.8 KiB
Python
from Crypto.PublicKey import ECC
|
|
from Crypto.Cipher import AES
|
|
from Crypto.Hash import SHA256
|
|
import binascii
|
|
|
|
|
|
def ecc_keygen(curve: str = 'P-256') -> ECC.EccKey:
|
|
return ECC.generate(curve=curve)
|
|
|
|
|
|
def ecc_encrypt(plaintext: str, recipient_public_key: ECC.EccKey):
|
|
eph_private = ECC.generate(curve=recipient_public_key.curve)
|
|
shared_point = recipient_public_key.pointQ * eph_private.d
|
|
shared_x = int(shared_point.x)
|
|
aes_key = SHA256.new(shared_x.to_bytes(32, 'big')).digest()
|
|
|
|
cipher = AES.new(aes_key, AES.MODE_GCM)
|
|
ciphertext, tag = cipher.encrypt_and_digest(plaintext.encode('utf-8'))
|
|
|
|
return {
|
|
'ephemeral_pub_der': eph_private.public_key().export_key(format='DER'),
|
|
'nonce': cipher.nonce,
|
|
'tag': tag,
|
|
'ciphertext': ciphertext,
|
|
}
|
|
|
|
|
|
def ecc_decrypt(enc: dict, recipient_private_key: ECC.EccKey) -> str:
|
|
eph_public = ECC.import_key(enc['ephemeral_pub_der'])
|
|
shared_point = eph_public.pointQ * recipient_private_key.d
|
|
shared_x = int(shared_point.x)
|
|
aes_key = SHA256.new(shared_x.to_bytes(32, 'big')).digest()
|
|
|
|
cipher = AES.new(aes_key, AES.MODE_GCM, nonce=enc['nonce'])
|
|
plaintext = cipher.decrypt_and_verify(enc['ciphertext'], enc['tag'])
|
|
return plaintext.decode('utf-8')
|
|
|
|
|
|
def main():
|
|
print('Welcome to ECC (ECIES-style)')
|
|
ptext = input('Enter plaintext: ')
|
|
|
|
priv_key = ecc_keygen()
|
|
pub_key = priv_key.public_key()
|
|
|
|
print('\nPublic Key (Qx, Qy):')
|
|
print('Qx =', hex(int(pub_key.pointQ.x)))
|
|
print('Qy =', hex(int(pub_key.pointQ.y)))
|
|
|
|
enc = ecc_encrypt(ptext, pub_key)
|
|
print("\nYour ciphertext (hex):", binascii.hexlify(enc['ciphertext']).decode())
|
|
|
|
decrypted = ecc_decrypt(enc, priv_key)
|
|
print('Your decrypted plaintext:', decrypted)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main() |