Skip to content

Instantly share code, notes, and snippets.

@odudex
Last active February 21, 2023 19:57
Show Gist options
  • Save odudex/9e848a91d23e967309bd1719910021e6 to your computer and use it in GitHub Desktop.
Save odudex/9e848a91d23e967309bd1719910021e6 to your computer and use it in GitHub Desktop.
Uses Embit and qrcode modules to generate ascii compact seed QR codes from nostr keys.
"""Uses Embit and qrcode modules to convert hex and bech32 keys to BIP39 seed words and vice-versa.
Also generates ascii compact seed QR codes and public keys.
Install Embit:
pip install embit
Exemple: Words as input:
python nostr_c_seed_qr.py picture body actor coil end satoshi fish mom distance proof thank play fantasy friend dinner clump boring ozone review cart virtual toss foot infant
Exemple: Hex key as input:
python nostr_c_seed_qr.py a4431c0a96a4997ed5ec763fb58b7f532530b9cf916219f3d2e2118f47cb56bb
Exemple: Bech32 key as input:
python nostr_c_seed_qr.py nsec153p3cz5k5jvha40vwclmtzml2vjnpww0j93pnu7juggc737t26ascxcmgr
"""
import sys
from io import StringIO
from embit import bip39, bech32, ec
from embit.wordlists.bip39 import WORDLIST
from qrcode import QRCode
def _compact_seed_qr():
# Krux code snippet
words = mnemonic.split(" ")
checksum_bits = 8 if len(words) == 24 else 4
indexes = [WORDLIST.index(word) for word in words]
bitstring = "".join([f"{bin(index)[2:]:0>11}" for index in indexes])[
:-checksum_bits
]
qr_data = int(bitstring, 2).to_bytes((len(bitstring) + 7) // 8, "big")
# Prints ascii QR code
qr_code = QRCode()
qr_code.add_data(qr_data)
qr_string = StringIO()
qr_code.print_ascii(out=qr_string, invert=True)
print("Compact Seed QR:")
print(qr_string.getvalue())
def _seed_numbers():
seed_numbers = []
words = mnemonic.split(" ")
for word in words:
seed_numbers.append(WORDLIST.index(word) + 1)
print("BIP39 Seed Numbers: " + str(seed_numbers))
def _public_keys():
print("Public Data:")
pub_key = ec.PrivateKey(bip39.mnemonic_to_bytes(mnemonic, ignore_checksum=True)).get_public_key().serialize()[1:]
print("Hex Public key: " + pub_key.hex())
converted_bits = bech32.convertbits(pub_key, 8, 5)
print(
"Bech32 Public key: "
+ bech32.bech32_encode(bech32.Encoding.BECH32, "npub", converted_bits)
)
def _print_common():
_seed_numbers()
_compact_seed_qr()
_public_keys()
print("Private Data:")
if len(sys.argv) == 2:
input_data = sys.argv[1]
if len(input_data) == 64: # bytes input
try:
entropy_bytes = bytes.fromhex(input_data)
except IOError:
print("Invalid string, must be a string of bytes")
sys.exit()
mnemonic = bip39.mnemonic_from_bytes(entropy_bytes)
print("Seed words: " + str(mnemonic))
converted_bits = bech32.convertbits(bytearray.fromhex(input_data), 8, 5)
print(
"Bech32 Private Key: "
+ bech32.bech32_encode(bech32.Encoding.BECH32, "nsec", converted_bits)
)
_print_common()
elif len(input_data) == 63 and input_data.startswith("nsec"): # bech32 input
spec, hrp, data = bech32.bech32_decode(input_data)
decoded = bech32.convertbits(data, 5, 8, False)
print("Hex Private Key: " + bytes(decoded).hex())
mnemonic = bip39.mnemonic_from_bytes(bytes(decoded))
print("Seed words: " + str(mnemonic))
_print_common()
else:
print("Invalid keys")
else:
if len(sys.argv) == 25:
mnemonic = " ".join(sys.argv[1:])
entropy = bip39.mnemonic_to_bytes(mnemonic, ignore_checksum=True)
print("Hex Private Key: " + str(entropy.hex()))
converted_bits = bech32.convertbits(entropy, 8, 5)
print(
"Bech32 Private key: "
+ bech32.bech32_encode(bech32.Encoding.BECH32, "nsec", converted_bits)
)
_print_common()
else:
print("Inform a 64 bytes private key or a 24 BIP39 words seed")
print(str(len(sys.argv) - 1) + " words were given")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment