import os import base64 import qrcode import platform from PIL import Image from pathlib import Path, PureWindowsPath, PurePath from cryptography.fernet import Fernet from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from xrpl import wallet from xrpl.core import keypairs from xrpl.utils import xrp_to_drops from xrpl.models.transactions import Payment from xrpl.transaction import safe_sign_transaction def create_wallet(silent: False): """ Generates a keypair """ if not silent: print("1. Generating seed...") seed = keypairs.generate_seed() print("2. Deriving keypair from seed...") pub, priv = keypairs.derive_keypair(seed) print("3. Deriving classic addresses from keypair..\n") address = keypairs.derive_classic_address(pub) else: seed = keypairs.generate_seed() pub, priv = keypairs.derive_keypair(seed) address = keypairs.derive_classic_address(pub) return address, seed def sign_transaction(xrp_amount, destination, ledger_seq, wallet_seq, password): """ Signs transaction and returns signed transaction blob in QR code """ print("1. Retrieving encrypted private key and salt...") with open(get_path("/WalletTEST/private.txt"), "r") as f: seed = f.read() seed = bytes.fromhex(seed) with open(get_path("/WalletTEST/salt.txt"), "rb") as f: salt = f.read() print("2. Initializing key...") kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, iterations=100000, salt=salt ) key = base64.urlsafe_b64encode(kdf.derive(bytes(password.encode()))) crypt = Fernet(key) print("3. Decrypting wallet's private key using password") seed = crypt.decrypt(seed) print("4. Initializing wallet using decrypted private key") _wallet = wallet.Wallet(seed=seed.decode(), sequence=0) validated_seq = ledger_seq _wallet.sequence = wallet_seq print("5. Constructing payment transaction...") my_tx_payment = Payment( account=_wallet.classic_address, amount=xrp_to_drops(xrp=xrp_amount), destination=destination, last_ledger_sequence=validated_seq + 100, # +100 to catch up with the ledger when we transmit the signed tx blob to Machine 2 sequence=_wallet.sequence, fee="10" ) print("6. Signing transaction...") my_tx_payment_signed = safe_sign_transaction(transaction=my_tx_payment, wallet=_wallet) img = qrcode.make(my_tx_payment_signed.to_dict()) print("7. Displaying signed transaction blob's QR code on the screen...") img.save(get_path("/WalletTEST/transactionID.png")) image = Image.open(get_path("/WalletTEST/transactionID.png")) image.show() print(f"RESULT: {my_tx_payment_signed.to_dict()}") print("END RESULT: Successful") def get_path(file): """ Get path (filesystem management) """ global File_ # Checks what OS is being used OS = platform.system() usr = Path.home() # Get PATH format based on the OS if OS == "Windows": File_ = PureWindowsPath(str(usr) + file) else: # Assuming Linux-style file format File_ = PurePath(str(usr) + file) return str(File_) def create_wallet_directory(): global File, Path_ OS = platform.system() usr = Path.home() if OS == "Windows": # If it's Windows, use this path: print("- OS Detected: Windows") File = PureWindowsPath(str(usr) + '/WalletTEST') Path_ = str(PureWindowsPath(str(usr))) if OS == "Linux": print("- OS Detected: Linux") # If it's Linux, use this path: File = PurePath(str(usr) + '/WalletTEST') Path_ = str(PurePath(str(usr))) if not os.path.exists(File): print("1. Generating wallet's keypair...") pub, seed = create_wallet(silent=True) print("2. Creating wallet's file directory...") os.makedirs(File) print("3. Generating and saving public key's QR code...") img = qrcode.make(pub) img.save(get_path("/WalletTEST/public.png")) print("4. Generating and saving wallet's salt...") salt = os.urandom(16) with open(get_path("/WalletTEST/salt.txt"), "wb") as f: f.write(salt) print("5. Generating wallet's filesystem password...") password = "This is a unit test password 123 !@# -+= }{/" kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, iterations=100000, salt=salt ) key = base64.urlsafe_b64encode(kdf.derive(bytes(password.encode()))) crypt = Fernet(key) print("6. Encrypting and saving private key by password...") priv = crypt.encrypt(bytes(seed, encoding='utf-8')) seed = crypt.encrypt(bytes(seed, encoding='utf-8')) with open(get_path("/WalletTEST/seed.txt"), "w") as f: f.write(seed.hex()) with open(get_path("/WalletTEST/private.txt"), "w") as f: f.write(priv.hex()) with open(get_path("/WalletTEST/public.txt"), "w") as f: f.write(pub) if os.path.exists(File): print(f"0. Wallet's filesystem already exist as the unit test has been performed before. Directory: {File}") def showcase_wallet_address_qr_code(): with open(get_path("/WalletTEST/public.txt"), "r") as f: print(f"0. Wallet Address: {f.read()}") __path = get_path("/WalletTEST/public.png") print(f"1. Getting address from {__path}...") print("2. Displaying QR code on the screen...") image = Image.open(get_path("/WalletTEST/public.png")) image.show() if __name__ == '__main__': print("Airgapped Machine Unit Test (5 functions):\n") print(f"UNIT TEST 1. create_wallet():") _address, _seed = create_wallet(silent=False) print(f"-- RESULTS --\n" f"Address: {_address}\n" f"Seed: {_seed}\n" f"END RESULT: Successful" ) print(f"\nUNIT TEST 2. create_wallet_directory():") create_wallet_directory() print("RESULT: Successful") print("\nUNIT TEST 3. showcase_wallet_address_qr_code():") showcase_wallet_address_qr_code() print("RESULT: Successful") print("\nUNIT TEST 4. get_path():") print("1. Getting files' path...\n") txt_file = get_path("/WalletTEST/FILE123.txt") png_file = get_path("/WalletTEST/PIC321.png") print(f"-- RESULTS --\n" f"txt_file: {txt_file}\n" f"png_file: {png_file}\n" f"END RESULT: Successful") print("\nUNIT TEST 5. sign_transaction():") print("Parameters: xrp_amount, destination, ledger_seq, wallet_seq, password") sign_transaction( xrp_amount=10, destination="rPEpirdT9UCNbnaZMJ4ENwKAwJqrTpvgMQ", ledger_seq=32602000, wallet_seq=32600100, password="This is a unit test password 123 !@# -+= }{/" )