Bitcoin Forum
September 11, 2025, 06:28:53 AM *
News: Latest Bitcoin Core release: 29.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: BASH38 - A bash wrapper for python BIP38 implementation (offline usage possible)  (Read 123 times)
apogio (OP)
Legendary
*
Online Online

Activity: 910
Merit: 1882


Duelbits.com - Rewarding, beyond limits.


View Profile WWW
July 28, 2025, 05:03:10 PM
Merited by Cricktor (2), ABCbits (1), Mia Chloe (1)
 #1

Why?

I've always wanted to create my own BIP38 tool. To be honest, the existing implementations out there are already excellent, so there wasn’t much need to reinvent the wheel. Instead, I decided to write a simple bash wrapper to make interacting with the Python library even easier.

And to spice things up, you can use the tool completely offline!

Requirements

1. A permanently offline Linux device for generating your private keys.
2. An online computer for downloading the necessary libraries (make sure pip is installed here).
3. A USB stick (or similar) to transfer the libraries from your online computer to your offline device.

Downloading the Libraries:

Start by booting up your online computer and opening a terminal. Create a directory for the dependencies and use pip to download the libraries:

Code:
mkdir -p requirements
pip download bip38 bitcoinlib -d requirements

Your offline device probably won’t have pip installed, but most Linux distributions do come with Python pre-installed. So, let’s get pip ready as well:

Code:
cd requirements
curl -O https://bootstrap.pypa.io/get-pip.py

Now, copy the entire requirements directory to your USB stick.

Installing the Libraries on the Offline Device:

Transfer the requirements directory from your USB stick to the offline computer. Open a terminal and install pip:

Code:
python3 get-pip.py --no-index

Next, create a virtual environment and activate it:

Code:
python3 -m venv .env
source .env/bin/activate

Now, install the Python libraries from your local directory:

Code:
pip install --no-index --find-links=requirements bip38 bitcoinlib

Running the Code

Time to have some fun! Create a bash file and make it executable:

Code:
touch bip38.sh
chmod u+x bip38.sh

Copy and paste the following script into bip38.sh:

Code:
#!/bin/bash

encrypt() {
    privkey="\$1"
    passphrase="\$2"
    python3 - <<EOF
from bip38 import BIP38
from bip38.cryptocurrencies import Bitcoin
bip38 = BIP38(Bitcoin)
print(bip38.encrypt("\$privkey", "\$passphrase"))
EOF
}

decrypt() {
    bip38key="\$1"
    passphrase="\$2"
    python3 - <<EOF
from bip38 import BIP38
from bip38.cryptocurrencies import Bitcoin
bip38 = BIP38(Bitcoin)
print(bip38.decrypt("\$bip38key", "\$passphrase"))
EOF
}

generate() {
    passphrase="\$1"
    # Get 32 bytes (256 bits) of entropy as hex from /dev/urandom
    entropy=\$(head -c 32 /dev/urandom | xxd -p -c 32)
    python3 - <<EOF
from bitcoinlib.keys import Key
from bip38 import BIP38
from bip38.cryptocurrencies import Bitcoin
import binascii

privkey_bytes = binascii.unhexlify("\$entropy")
key = Key(privkey_bytes)
privkey_wif = key.wif()
address = key.address()

bip38 = BIP38(Bitcoin)
encrypted = bip38.encrypt(privkey_wif, "\$passphrase")

print("Address: " + address)
print("Encrypted Private Key (BIP38): " + encrypted)
EOF
}

if [ "\$#" -lt 2 ]; then
    echo "Usage:"
    echo "  \$0 encrypt <privatekey> <passphrase>"
    echo "  \$0 decrypt <bip38key> <passphrase>"
    echo "  \$0 generate <passphrase>"
    exit 1
fi

case "\$1" in
    encrypt)
        if [ "\$#" -ne 3 ]; then
            echo "Usage: \$0 encrypt <privatekey> <passphrase>"
            exit 1
        fi
        encrypt "\$2" "\$3"
        ;;
    decrypt)
        if [ "\$#" -ne 3 ]; then
            echo "Usage: \$0 decrypt <bip38key> <passphrase>"
            exit 1
        fi
        decrypt "\$2" "\$3"
        ;;
    generate)
        if [ "\$#" -ne 2 ]; then
            echo "Usage: \$0 generate <passphrase>"
            exit 1
        fi
        generate "\$2"
        ;;
    *)
        echo "Unknown command: \$1"
        exit 1
        ;;
esac

Usage:

Code:
./bip38.sh encrypt <privatekey> <passphrase>
./bip38.sh decrypt <bip38key> <passphrase>
./bip38.sh generate <passphrase>

Example Execution:

Code:
> ./bip38.sh generate good_morning_from_apogio

Address: 1KDqMoReZhqt5grhVQvnTTNhLkqSi6mns5
Encrypted Private Key (BIP38): 6PYNQn8y5scqonRbhgJMwu7jdrSeYfodf4FGC46TcB8iqf6JizLKMXRWJE


> ./bip38.sh decrypt 6PYNQn8y5scqonRbhgJMwu7jdrSeYfodf4FGC46TcB8iqf6JizLKMXRWJE good_morning_from_apogio

KyZNsaPBGJVC2GLLBZ9wZfKYF5NgVzU1bQUpEBHMC9Hp1fKQY2qY


> ./bip38.sh encrypt KyZNsaPBGJVC2GLLBZ9wZfKYF5NgVzU1bQUpEBHMC9Hp1fKQY2qY good_morning_from_apogio

6PYNQn8y5scqonRbhgJMwu7jdrSeYfodf4FGC46TcB8iqf6JizLKMXRWJE



Disclaimers

1. This is just for fun!
2. You could just use Python directly, but where’s the fun in that? The Bash wrapper is part of the charm.


Mia Chloe
Hero Member
*****
Offline Offline

Activity: 826
Merit: 1345


Contact me for your designs...


View Profile
July 28, 2025, 07:52:55 PM
Last edit: July 28, 2025, 08:29:07 PM by Mia Chloe
 #2

A permanently offline Linux device for generating your private keys.
Nice script apogio. I can't really say much since my python coding is kinda on the drop streak over the years since I don't play around with codes more frequently like I used to. Anyways so far the offline capability is kinda like the standout feature here so would  it be possible to apply updates to the bip38 or bitcoinlib libraries in your offline environment given the  permanently offline requirement?

All that  comes to my mind currently is updates would need to be done manually if necessary. Also can any other entropy be used? Plus are there ways to verify before installation? Just asking for theoretical purposes though.

apogio (OP)
Legendary
*
Online Online

Activity: 910
Merit: 1882


Duelbits.com - Rewarding, beyond limits.


View Profile WWW
July 29, 2025, 06:22:30 AM
 #3

Anyways so far the offline capability is kinda like the standout feature here so would  it be possible to apply updates to the bip38 or bitcoinlib libraries in your offline environment given the  permanently offline requirement?

All that  comes to my mind currently is updates would need to be done manually if necessary. 

You can follow the same pattern again. As long as you have an offline device and an offline one, you can take the same steps.

Also can any other entropy be used?

Sure, as you can see, I am using entropy=\$(head -c 32 /dev/urandom | xxd -p -c 32)
Obviously the python library bitcoinlib can generate the entropy as well, but I am not aware of the specific implementation, it may be using /dev/urandom as well.

Plus are there ways to verify before installation?

You must mean to verify the python libraries. There must be ways to verify them, but I thought the tutorial was stretched enough anyways, so I didn't search for it.



I am seeing in the script now, that when I copied it from nano into sublime and then into this post, there are some backslashes, probably put in the code by sublime. It may not be an issue, I will need to check.

BattleDog
Newbie
*
Offline Offline

Activity: 28
Merit: 43


View Profile
September 02, 2025, 12:13:26 PM
 #4

Fun project and I like the fully offline flow.

Some suggestions and important points:

Build wheels on the online box and record hashes:
Code:
pip wheel --wheel-dir wheels bip38==X.Y.Z bitcoinlib==A.B.C
Code:
sha256sum wheels/* > wheels.SHA256

On the offline box:
Code:
pip install --no-index --find-links=./wheels --require-hashes -r requirements.txt
Pinning + hashes beats a blind pip install and avoids surprise upgrades.

Right now you pass the passphrase as an arg, which shows up in shell history and ps. Prompt instead: read -rsp "Passphrase: " passphrase; echo. Feed it to Python via stdin or env, then unset passphrase afterward.

Add
Code:
set -euo pipefail
at the top, check return codes, and fail fast if the venv is missing or the libs are not importable.
Run the official BIP38 vectors and one cross-implementation check (Electrum or another lib). Print a clear PASS/FAIL before letting users trust real keys.
Document exactly what the encrypt/decrypt functions accept: WIF vs raw 32-byte hex, mainnet vs testnet prefix, and which BIP38 mode you use (non-EC vs EC-multiply with optional lot/sequence). Ambiguity here is how people lock themselves out.

Ensure the generator uses os.urandom only. Offer a --qr flag to dump the BIP38 string as a QR for paper backup. Never echo the passphrase back to the screen or logs.

BIP38 is for single private keys, not BIP39 seeds. For HD wallets prefer a seed with a passphrase or Shamir. If someone insists on BIP38, tell them to use a long, unique passphrase.
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!