TODO: Explain process step by step
import os
import bech32
import ecdsa
import hashlib
from ecdsa.curves import SECP256k1
from ecdsa.keys import SigningKey, VerifyingKey
from Crypto.Protocol.KDF import scrypt
from Crypto.Cipher import AES
# Encrypted Message Parameters
plaintext_message=b"Hey Bob, this is Alice, you and I can read this message, but no one else."
alice_private_key_hex = "0000000000000000000000000000000000000000000000000000000000000001"
bob_wallet_address = "rdx1qspvvprlj3q76ltdxpz5qm54cp7dshrh3e9cemeu5746czdet3cfaegp8alwf"
# Convert receiver's wallet address into a public key
_hrp, bob_readdr_5bit = bech32.bech32_decode(bob_wallet_address)
bob_readdr_bytes = bech32.convertbits(bob_readdr_5bit, 5, 8, pad=False)
bob_public_key_bytes = bytes(bob_readdr_bytes)[1:34]
bob_public_key = VerifyingKey.from_string(bob_public_key_bytes, curve=SECP256k1, hashfunc=hashlib.sha256)
print("Bob Public Key:", bob_public_key.to_string("compressed").hex())
alice_private_key = SigningKey.from_string(bytearray.fromhex(alice_private_key_hex), curve=SECP256k1, hashfunc=hashlib.sha256)
# Diffie-Hellman - we need to use lower level functions here so that we get back dh as a curve point
dh = bob_public_key.pubkey.point * alice_private_key.privkey.secret_multiplier
# Create Ephemeral Key
ephemeral_private_key = ecdsa.SigningKey.generate(curve=ecdsa.SECP256k1, hashfunc=hashlib.sha256)
ephemeral_public_key = ephemeral_private_key.get_verifying_key()
print("Ephemeral Public Key:", ephemeral_public_key.to_string("compressed").hex())
ephemeral_point = ephemeral_public_key.pubkey.point
# Elliptic Curve Point Addition
shared_secret_point = dh + ephemeral_point
shared_secret_integer = shared_secret_point.x()
shared_secret = int.to_bytes(shared_secret_integer, 32, 'big')
print("Shared Secret: ", shared_secret.hex())
# Create nonce
nonce_bytes = os.urandom(12)
print("Nonce: ", nonce_bytes.hex())
# Generate Scrypt salt from nonce
salt = hashlib.sha256(nonce_bytes).digest()
print("Scrypt Salt: ", salt.hex())
# Create the Encryption Key
key = scrypt(shared_secret, salt, 32, 8192, 8 ,1)
print("Encryption Key:", key.hex())
encrypt = AES.new(key, AES.MODE_GCM, nonce=nonce_bytes)
# Add the Ephemeral Public Key as Associated Data
encrypt.update(ephemeral_public_key.to_string("compressed"))
# Perform the Encryption and generate Ciphertext with related Authentication Tag
ciphertext, auth_tag = encrypt.encrypt_and_digest(plaintext_message)
print("Ciphertext:", ciphertext.hex())
print("Auth Tag:", auth_tag.hex())
# Encode the message to submit
msg = bytearray(b'\x01')
msg += bytearray(b'\xff')
msg += bytearray(ephemeral_public_key.to_string("compressed"))
msg += bytearray(nonce_bytes)
msg += bytearray(auth_tag)
msg += bytearray(ciphertext)
print("Encoded Encrypted Message:", msg.hex())
Run this on Replit:
Also see: How to decrypt an encrypted transaction message created by the Radix Wallet
This work by RadixPool.com is licensed under a Creative Commons Attribution 4.0 International License.