From 15edaaaba12ce304d8649c581bc6353aeb202f82 Mon Sep 17 00:00:00 2001 From: Wo Jake <87929946+wojake@users.noreply.github.com> Date: Fri, 28 Oct 2022 12:07:32 +0000 Subject: [PATCH] Create airgapped-wallet.py --- .../airgapped-wallet/py/airgapped-wallet.py | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 content/_code-samples/airgapped-wallet/py/airgapped-wallet.py diff --git a/content/_code-samples/airgapped-wallet/py/airgapped-wallet.py b/content/_code-samples/airgapped-wallet/py/airgapped-wallet.py new file mode 100644 index 0000000000..53d5cac147 --- /dev/null +++ b/content/_code-samples/airgapped-wallet/py/airgapped-wallet.py @@ -0,0 +1,201 @@ +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(): + """ + Generates a keypair + """ + + seed = keypairs.generate_seed() + pub, priv = keypairs.derive_keypair(seed) + + address = keypairs.derive_classic_address(pub) + private = keypairs.derive_classic_address(priv) + print( + f"\n\n XRP WALLET CREDENTIALS" + f"\n Wallet Address: {address}" + f"\n Private Key: {private}" + f"\n Seed: {seed}" + ) + + return address, private, seed + + +def sign_transaction(_xrp_amount, _destination, _ledger_seq, _wallet_seq, password): + """ + Signs transaction and returns signed transaction blob in QR code + """ + + with open(get_path("/Wallet/private.txt"), "r") as f: + _seed = f.read() + _seed = bytes.fromhex(_seed) + + with open(get_path("/Wallet/salt.txt"), "rb") as f: + salt = f.read() + + # Line 49-58: initialize key + kdf = PBKDF2HMAC( + algorithm=hashes.SHA256(), + length=32, + iterations=100000, + salt=salt + ) + + key = base64.urlsafe_b64encode(kdf.derive(bytes(password.encode()))) + crypt = Fernet(key) + + # Decrypts the wallet's private key + _seed = crypt.decrypt(_seed) + + # Initialize XRPL wallet using decrypted private key + _wallet = wallet.Wallet(seed=_seed.decode(), sequence=0) + + validated_seq = _ledger_seq + _wallet.sequence = _wallet_seq + + # Construct 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" + ) + + # Signs transaction and displays the signed_tx blob in QR code + # Scan the QR code and transmit the signed_tx blob to an online machine (Machine 2) to relay it to the XRPL + my_tx_payment_signed = safe_sign_transaction(transaction=my_tx_payment, wallet=_wallet) + + img = qrcode.make(my_tx_payment_signed.to_dict()) + img.save(get_path("/Wallet/transactionID.png")) + image = Image.open(get_path("/Wallet/transactionID.png")) + image.show() + + +def get_path(file): + """ + Get path (filesystem management) + """ + + global File_ + # Checks what OS is being us + OS = platform.system() + usr = Path.home() + + # Get PATH format based on the OS + if OS == "Windows": + File_ = PureWindowsPath(str(usr) + file) + if OS == "Linux": + File_ = PurePath(str(usr) + file) + + return str(File_) + + +def main(): + global File, Path_ + + # Gets the machine's operating system (OS) + OS = platform.system() + usr = Path.home() + if OS == "Windows": + # If it's Windows, use this path: + File = PureWindowsPath(str(usr) + '/Wallet') + Path_ = str(PureWindowsPath(str(usr))) + if OS == "Linux": + # If it's Linux, use this path: + File = PurePath(str(usr) + '/Wallet') + Path_ = str(PurePath(str(usr))) + + # If the Wallet's folder doesn't exist, continue on + if os.path.exists(File): + while True: + ask = int(input("\n 1. Transact XRP" + "\n 2. Generate an XRP wallet (read only)" + "\n 3. Showcase XRP Wallet Address (QR Code)" + "\n\n Enter Index: " + )) + + if ask == 1: + password = str(input(" Enter Password: ")) + amount = float(input("\n Enter XRP To Send: ")) + destination = input("\n Enter Destination: ") + wallet_sequence = int(input("\n Enter Wallet Sequence: ")) + ledger_sequence = int(input("\n Enter Ledger Sequence: ")) + + sign_transaction(_xrp_amount=amount, + _destination=destination, + _ledger_seq=ledger_sequence, + _wallet_seq=wallet_sequence, + password=password + ) + + del destination, amount, wallet_sequence, ledger_sequence + + if ask == 2: + _pub, _priv, _seed = create_wallet() + + if ask == 3: + with open(get_path("/Wallet/public.txt"), "r") as f: + print(f"\n Wallet Address: {f.read()}") + + image = Image.open(get_path("/Wallet/public.png")) + image.show() + + else: + # If the Wallet's folder does not exist, create one and store wallet data (encrypted private key, encrypted seed, account address) + pub, priv, seed = create_wallet() + + os.makedirs(File) + + img = qrcode.make(pub) + img.save(get_path("/Wallet/public.png")) + openimg = Image.open(get_path("/Wallet/public.png")) + + openimg.show() + + password = str(input("\n Enter Password: ")) + salt = os.urandom(16) + + with open(get_path("/Wallet/salt.txt"), "wb") as f: + f.write(salt) + + kdf = PBKDF2HMAC( + algorithm=hashes.SHA256(), + length=32, + iterations=100000, + salt=salt + ) + + key = base64.urlsafe_b64encode(kdf.derive(bytes(password.encode()))) + + crypt = Fernet(key) + + priv = crypt.encrypt(bytes(seed, encoding='utf-8')) + seed = crypt.encrypt(bytes(seed, encoding='utf-8')) + + with open(get_path("/Wallet/seed.txt"), "w") as f: + f.write(seed.hex()) + + with open(get_path("/Wallet/private.txt"), "w") as f: + f.write(priv.hex()) + + with open(get_path("/Wallet/public.txt"), "w") as f: + f.write(pub) + + +if __name__ == '__main__': + main()