Compare commits
10 Commits
bd8901d827
...
f9a69e4992
| Author | SHA1 | Date | |
|---|---|---|---|
| f9a69e4992 | |||
|
|
ae34fb5762 | ||
|
|
937a9c8ba1 | ||
| 28532bd6d3 | |||
| 42b1a26f8d | |||
|
|
3b32ca76b3 | ||
|
|
6ac9ae4566 | ||
|
|
652222a0a6 | ||
|
|
06408a3bfa | ||
|
|
b595c91d19 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,4 +1,6 @@
|
||||
nostr_keygen.egg-info
|
||||
|
||||
build/
|
||||
nostr_keygen.egg-info/
|
||||
__pycache__/
|
||||
venv/
|
||||
*.pyc
|
||||
|
||||
181
README.md
181
README.md
@@ -1,115 +1,96 @@
|
||||
# nostr-keygen
|
||||
# Nostr‑KeyGen
|
||||
|
||||
A tiny command‑line utility written in Python that generates an Nostr **npub** (public key) and **nsec** (private key) pair from a single file that you drop into the terminal.
|
||||
A tiny, interactive command‑line tool that lets you generate a Nostr key pair from any file you choose. It works on macOS, Linux and (with a few tweaks) Windows. The script is intentionally lightweight – no fancy GUI, just an easy‑to‑follow wizard run from your terminal.
|
||||
|
||||
> **NOTE:** The instructions below work on **macOS** and **Linux**. If you’re on Windows you’ll need a compatible terminal (e.g. WSL, Git‑Bash, or PowerShell with Python).
|
||||
## Features
|
||||
|
||||
---
|
||||
- **Drag‑and‑drop friendly**: on macOS and Linux you can simply drop the file into the Terminal window.
|
||||
- **Clear, step‑by‑step tutorial**: every run prints a short intro, an explanation, a quick demo, asks for your file and finishes with the key‑pair in *both* Nostr‑Bech32 and raw hex.
|
||||
- **No external binaries** – it just uses the pure‑Python `ecdsa` and `bech32` packages.
|
||||
- **Portable**: you can ship it as a single Python file or compile to a binary with tools such as PyInstaller.
|
||||
|
||||
## 🚀 Run
|
||||
## Install
|
||||
|
||||
### 1. Clone the repo
|
||||
```bash
|
||||
# Drop any file into the terminal prompt!
|
||||
# macOS (most terminals) and many Linux terminals allow drag‑and‑drop.
|
||||
|
||||
nostr-keygen <filepath>
|
||||
```
|
||||
|
||||
> The terminal prompt you see (``$`` on macOS, ``username@host:~$`` on Linux) accepts drag‑and‑drop of any file. The script will read that file path and produce key strings.
|
||||
|
||||
---
|
||||
|
||||
### 📄 Quick test
|
||||
|
||||
```bash
|
||||
# Create a tiny dummy file
|
||||
printf "random entropy" > /tmp/dummy.bin
|
||||
|
||||
# Run the program
|
||||
nostr-keygen /tmp/dummy.bin
|
||||
```
|
||||
|
||||
You should see two lines outputted: an `nsec` string and an `npub` string.
|
||||
|
||||
## 🛠️ Installation
|
||||
|
||||
### 1️⃣ Install Python
|
||||
|
||||
- **macOS**: Use Homebrew
|
||||
```bash
|
||||
brew install python@3.12
|
||||
```
|
||||
(If you already have Python, skip this step.)
|
||||
|
||||
- **Linux (Ubuntu / Debian‑based)**:
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install python3 python3-venv
|
||||
```
|
||||
(Other distros may use `yum`, `dnf`, or your package manager of choice.)
|
||||
|
||||
- **Linux (Arch)**:
|
||||
```bash
|
||||
sudo pacman -S python
|
||||
```
|
||||
|
||||
> **Tip** – On macOS the `python3` binary is typically symlinked to `python`, but on many Linux systems you’ll need `python3` explicitly.
|
||||
|
||||
### 2️⃣ Clone the repo
|
||||
|
||||
```bash
|
||||
git clone https://github.com/your‑github‑handle/nostr-keygen.git
|
||||
git clone https://github.com/btcforplebs/nostr-keygen.git
|
||||
cd nostr-keygen
|
||||
```
|
||||
|
||||
### 3️⃣ Create a virtual‑environment (recommended)
|
||||
|
||||
### 2. Create and activate a virtual environment
|
||||
```bash
|
||||
python3 -m venv .venv # create a venv in the repo directory
|
||||
source .venv/bin/activate # activate it (both on macOS and Linux)
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
> The virtual‑environment isolates third‑party libraries (`ecdsa`, `bech32`, etc.) from the system‑wide Python installation, ensuring that installing or updating them won’t accidentally break other projects. It also guarantees that anyone who checks out the repo can recreate the exact same runtime environment.
|
||||
|
||||
### 4️⃣ Install the tool in editable mode (development) or normally
|
||||
|
||||
- **Editable (work on the code as you edit it)**
|
||||
```bash
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
- **Normal installation**
|
||||
```bash
|
||||
pip install .
|
||||
```
|
||||
|
||||
Both forms install the console‑script `nostr‑keygen` into `./.venv/bin`.
|
||||
|
||||
## 🚫 Key‑string safety
|
||||
|
||||
> Keep your `nsec` secret in a secure, offline location. Anyone with that string can sign Nostr events or spend Nostr‑based funds.
|
||||
|
||||
|
||||
## 🔧 How the code works (quick dive)
|
||||
|
||||
```python
|
||||
# main.py
|
||||
import argparse, hashlib
|
||||
from ecdsa import SigningKey, SECP256k1
|
||||
from bech32 import bech32_encode, convertbits
|
||||
|
||||
NSEC_PREFIX, NPUB_PREFIX = "nsec", "npub"
|
||||
|
||||
def _to_bech32(data: bytes, hrp: str) -> str:
|
||||
five_bits = convertbits(list(data), 8, 5, True)
|
||||
return bech32_encode(hrp, five_bits)
|
||||
|
||||
# … (rest unchanged) …
|
||||
### 3. Install dependencies
|
||||
```bash
|
||||
pip install ecdsa bech32
|
||||
```
|
||||
|
||||
### Why the key‑gen algorithm matters
|
||||
### 4. Make it executable and optional symlink
|
||||
```bash
|
||||
chmod +x nostr-keygen
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Run the script directly from the project directory:
|
||||
```bash
|
||||
./nostr-keygen
|
||||
```
|
||||
|
||||
You’ll be guided through the following steps:
|
||||
|
||||
1. Welcome screen – press **Enter** to continue.
|
||||
2. Tool overview – press **Enter** to continue.
|
||||
3. Demo key generation using a temporary file – press **Enter** to continue.
|
||||
4. Prompt for the path to your entropy file. You can drop a file into the terminal on macOS/Linux or type the absolute/relative path.
|
||||
5. The script shows you the generated `nsec`, `npub`, and the hex representation of both keys.
|
||||
6. Press **Enter** again, the terminal is cleared and the program exits.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
🛠️ Welcome to Nostr‑KeyGen
|
||||
|
||||
This tool will:
|
||||
- Pick any file as your entropy source
|
||||
- Hash the file → 32‑byte seed
|
||||
- Derive a secp256k1 key pair
|
||||
- Print the Nostr `nsec`/`npub` and the raw hex values
|
||||
|
||||
💡 Demo (using a random file in /tmp)...
|
||||
Demo nsec: nsec1q4...
|
||||
Demo npub: npub1p8...
|
||||
Demo priv hex: 1f4c...
|
||||
Demo pub hex: 03a1...
|
||||
|
||||
📂 Enter the path to your entropy file: /Users/me/secret.txt
|
||||
|
||||
✅ Your new key pair:
|
||||
- nsec : nsec1q8...
|
||||
- npub : npub1p9...
|
||||
- priv hex : 7c2d...
|
||||
- pub hex : 029b...
|
||||
|
||||
🧹 Cleaning up...
|
||||
🚀 Done. Goodbye!
|
||||
```
|
||||
|
||||
## FAQs
|
||||
|
||||
| Question | Answer |
|
||||
|----------|--------|
|
||||
| Why does it read *any* file as entropy? | Nostr requires a 32‑byte seed. Hashing a file guarantees a deterministic, pseudo‑random 32‑byte output irrespective of the file size or content. |
|
||||
| Will it always produce the same key for the same file? | Yes – the seed is derived from a SHA‑256 hash of the file contents. |
|
||||
| Can I use a password instead? | The current script accepts files only, but you could easily pipe a password string using `echo -n 'mypassword' > pwd.txt` first. |
|
||||
|
||||
## Contributing
|
||||
|
||||
Pull requests are welcome! Just make sure the code passes `flake8`/`black` and contains an updated README.
|
||||
|
||||
## License
|
||||
|
||||
MIT – see [LICENSE](LICENSE).
|
||||
|
||||
1. **Read file in binary** – We need raw entropy; reading as text would truncate or encode the file in an unexpected way. Binary mode is the most faithful representation of the file’s content.
|
||||
2. **SHA‑256 hash** – Provides a *deterministic* 32‑byte seed from any file. Different inputs yield different seeds, and the same input always yields the same seed.
|
||||
3. **Create a secp256k1 signing key** – The curve used by Nostr (and Bitcoin) for ECDSA. The secret key is derived directly from the seed bytes.
|
||||
4. **Bech32 encoding** – Nostr keys are human‑readable Bech32 strings prefixed with `nsec` or `npub`. `_to_bech32` converts 8‑bit byte streams to 5‑bit groups and encodes them.
|
||||
@@ -1,79 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate a fresh Nostr key (npub/nsec) using a file as entropy source.
|
||||
|
||||
Usage:
|
||||
nostr-keygen <path-to-file>
|
||||
|
||||
The file is read in binary mode and its bytes are hashed with SHA‑256 to
|
||||
produce the 32‑byte seed which is then used to generate a secp256k1
|
||||
private key. The private key is hex‑encoded as an nsec, and the
|
||||
corresponding public key is converted to a Bech32 npub. The example
|
||||
uses the official Nostr prefix "nsec"/
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
|
||||
from ecdsa import SigningKey, SECP256k1
|
||||
from bech32 import bech32_encode, convertbits
|
||||
|
||||
# Constants for Nostr bech32 encoding
|
||||
NSEC_PREFIX = "nsec"
|
||||
NPUB_PREFIX = "npub"
|
||||
|
||||
# convertbits helper adapted from bech32 library; using provided function for clarity
|
||||
|
||||
def _to_bech32(data: bytes, hrp: str) -> str:
|
||||
"""Encode raw bytes into a Bech32 string with the given human‑readable part."""
|
||||
# Convert 8‑bit bytes to 5‑bit groups
|
||||
five_bits = convertbits(list(data), 8, 5, True)
|
||||
if five_bits is None:
|
||||
raise ValueError("Error converting data to 5‑bit groups")
|
||||
return bech32_encode(hrp, five_bits)
|
||||
|
||||
|
||||
def _entropy_to_pri_key(entropy: bytes) -> SigningKey:
|
||||
"""Return an ECDSA SECP256k1 private key derived from entropy."""
|
||||
# Use SHA‑256 of the provided entropy for deterministic key generation
|
||||
seed = hashlib.sha256(entropy).digest()
|
||||
return SigningKey.from_string(seed, curve=SECP256k1)
|
||||
|
||||
|
||||
def generate_key_from_file(file_path: str) -> (str, str):
|
||||
"""Return (nsec, npub) for the key derived from the file content."""
|
||||
if not os.path.isfile(file_path):
|
||||
raise FileNotFoundError(f"File not found: {file_path}")
|
||||
with open(file_path, "rb") as f:
|
||||
data = f.read()
|
||||
|
||||
sk = _entropy_to_pri_key(data)
|
||||
vk = sk.get_verifying_key()
|
||||
# Private key bytes
|
||||
private_bytes = sk.to_string()
|
||||
# Public key bytes, compressed (33 bytes)
|
||||
public_bytes = vk.to_string("compressed")
|
||||
nsec = _to_bech32(private_bytes, NSEC_PREFIX)
|
||||
npub = _to_bech32(public_bytes, NPUB_PREFIX)
|
||||
return nsec, npub
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Generate Nostr key pair from file entropy")
|
||||
parser.add_argument("file", help="Path to file used as entropy source")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
nsec, npub = generate_key_from_file(args.file)
|
||||
except Exception as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print("NSec:", nsec)
|
||||
print("NPub:", npub)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
79
main.py
79
main.py
@@ -1,79 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate a fresh Nostr key (npub/nsec) using a file as entropy source.
|
||||
|
||||
Usage:
|
||||
nostr-keygen <path-to-file>
|
||||
|
||||
The file is read in binary mode and its bytes are hashed with SHA‑256 to
|
||||
produce the 32‑byte seed which is then used to generate a secp256k1
|
||||
private key. The private key is hex‑encoded as an nsec, and the
|
||||
corresponding public key is converted to a Bech32 npub. The example
|
||||
uses the official Nostr prefix "nsec"/
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
|
||||
from ecdsa import SigningKey, SECP256k1
|
||||
from bech32 import bech32_encode, convertbits
|
||||
|
||||
# Constants for Nostr bech32 encoding
|
||||
NSEC_PREFIX = "nsec"
|
||||
NPUB_PREFIX = "npub"
|
||||
|
||||
# convertbits helper adapted from bech32 library; using provided function for clarity
|
||||
|
||||
def _to_bech32(data: bytes, hrp: str) -> str:
|
||||
"""Encode raw bytes into a Bech32 string with the given human‑readable part."""
|
||||
# Convert 8‑bit bytes to 5‑bit groups
|
||||
five_bits = convertbits(list(data), 8, 5, True)
|
||||
if five_bits is None:
|
||||
raise ValueError("Error converting data to 5‑bit groups")
|
||||
return bech32_encode(hrp, five_bits)
|
||||
|
||||
|
||||
def _entropy_to_pri_key(entropy: bytes) -> SigningKey:
|
||||
"""Return an ECDSA SECP256k1 private key derived from entropy."""
|
||||
# Use SHA‑256 of the provided entropy for deterministic key generation
|
||||
seed = hashlib.sha256(entropy).digest()
|
||||
return SigningKey.from_string(seed, curve=SECP256k1)
|
||||
|
||||
|
||||
def generate_key_from_file(file_path: str) -> (str, str):
|
||||
"""Return (nsec, npub) for the key derived from the file content."""
|
||||
if not os.path.isfile(file_path):
|
||||
raise FileNotFoundError(f"File not found: {file_path}")
|
||||
with open(file_path, "rb") as f:
|
||||
data = f.read()
|
||||
|
||||
sk = _entropy_to_pri_key(data)
|
||||
vk = sk.get_verifying_key()
|
||||
# Private key bytes
|
||||
private_bytes = sk.to_string()
|
||||
# Public key bytes, compressed (33 bytes)
|
||||
public_bytes = vk.to_string("compressed")
|
||||
nsec = _to_bech32(private_bytes, NSEC_PREFIX)
|
||||
npub = _to_bech32(public_bytes, NPUB_PREFIX)
|
||||
return nsec, npub
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Generate Nostr key pair from file entropy")
|
||||
parser.add_argument("file", help="Path to file used as entropy source")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
nsec, npub = generate_key_from_file(args.file)
|
||||
except Exception as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print("NSec:", nsec)
|
||||
print("NPub:", npub)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
148
nostr-keygen
Executable file
148
nostr-keygen
Executable file
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env python3
|
||||
"""A friendly CLI that walks you through generating a Nostr key pair
|
||||
from a file you pick on the fly.
|
||||
|
||||
Run it with:
|
||||
./nostr-keygen
|
||||
"""
|
||||
|
||||
import time
|
||||
import argparse
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# ----- IMPORTS ---------------------------------------------------------------
|
||||
try:
|
||||
from ecdsa import SigningKey, SECP256k1
|
||||
from bech32 import bech32_encode, convertbits
|
||||
except ImportError as e:
|
||||
print("⚠️ Required packages are missing.\nRun 'python3 -m venv venv && source venv/bin/activate && pip install ecdsa bech32' first.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# ----- CONSTANTS -------------------------------------------------------------
|
||||
NSEC_PREFIX = "nsec"
|
||||
NPUB_PREFIX = "npub"
|
||||
|
||||
# ----- HELPER FUNCTIONS ------------------------------------------------------
|
||||
def _to_bech32(data: bytes, hrp: str) -> str:
|
||||
"""Encode raw bytes into a Bech32 string with the given human‑readable part."""
|
||||
five_bits = convertbits(list(data), 8, 5, True)
|
||||
if five_bits is None:
|
||||
raise ValueError("Error converting data to 5‑bit groups")
|
||||
return bech32_encode(hrp, five_bits)
|
||||
|
||||
def _entropy_to_pri_key(entropy: bytes) -> SigningKey:
|
||||
"""Return a SECP256k1 private key derived from SHA‑256 of the entropy."""
|
||||
seed = hashlib.sha256(entropy).digest()
|
||||
return SigningKey.from_string(seed, curve=SECP256k1)
|
||||
|
||||
def generate_key_from_file(file_path: Path):
|
||||
"""Return (nsec, npub, private_hex, public_hex)."""
|
||||
if not file_path.is_file():
|
||||
raise FileNotFoundError(f"File not found: {file_path}")
|
||||
|
||||
with file_path.open("rb") as f:
|
||||
data = f.read()
|
||||
|
||||
sk = _entropy_to_pri_key(data)
|
||||
vk = sk.get_verifying_key()
|
||||
|
||||
private_bytes = sk.to_string()
|
||||
public_bytes = vk.to_string("compressed")
|
||||
|
||||
nsec = _to_bech32(private_bytes, NSEC_PREFIX)
|
||||
npub = _to_bech32(public_bytes, NPUB_PREFIX)
|
||||
|
||||
return nsec, npub, private_bytes.hex(), public_bytes.hex()
|
||||
|
||||
# ----- CLI / UI ---------------------------------------------------------------
|
||||
def welcome():
|
||||
clear_terminal()
|
||||
print("\n" + "="*30)
|
||||
print("🛠️ Welcome to Nostr‑KeyGen")
|
||||
print("="*30 + "\n")
|
||||
|
||||
|
||||
def info():
|
||||
clear_terminal()
|
||||
print("This tool will help you:")
|
||||
print("- Pick any file as your entropy source")
|
||||
print("- Hash the file → 32‑byte seed")
|
||||
print("- Derive a secp256k1 key pair")
|
||||
print("- Print the Nostr `nsec`/`npub` and the raw hex values")
|
||||
print()
|
||||
|
||||
def demo():
|
||||
clear_terminal()
|
||||
print("💡 EXAMPLE (using a random file in /tmp)...")
|
||||
example = Path("/tmp/nostr_demo_entropy.txt")
|
||||
example.write_bytes(os.urandom(256))
|
||||
try:
|
||||
nsec, npub, priv_hex, pub_hex = generate_key_from_file(example)
|
||||
print(f" DEMO nsec: {nsec}")
|
||||
print(f" DEMO npub: {npub}")
|
||||
print(f" DEMO priv hex: {priv_hex}")
|
||||
print(f" DEMO pub hex: {pub_hex}")
|
||||
except Exception as e:
|
||||
print(f" Cannot generate demo key: {e}")
|
||||
finally:
|
||||
example.unlink(missing_ok=True)
|
||||
print()
|
||||
|
||||
def ask_file() -> Path:
|
||||
clear_terminal()
|
||||
while True:
|
||||
path = input("📂 Enter the path or drop your file: ").strip()
|
||||
if not path:
|
||||
print(" ✨ Nothing entered, try again.")
|
||||
continue
|
||||
p = Path(path).expanduser().resolve()
|
||||
if p.is_file():
|
||||
return p
|
||||
print(" ❌ That path isn’t a file. Try again.")
|
||||
|
||||
def show_keys(nsec, npub, priv_hex, pub_hex):
|
||||
print("\n✅ Your new key pair:")
|
||||
print(f" - nsec : {nsec}")
|
||||
print(f" - npub : {npub}")
|
||||
print(f" - priv hex : {priv_hex}")
|
||||
print(f" - pub hex : {pub_hex}\n")
|
||||
|
||||
def clear_terminal():
|
||||
"""Clear the screen for a safe exit."""
|
||||
os.system("clear" if os.name != "nt" else "cls")
|
||||
|
||||
def pause(prompt="[Enter] to continue"):
|
||||
"""Wait for the user to hit Enter."""
|
||||
input(f"\n{prompt}")
|
||||
|
||||
def main():
|
||||
welcome()
|
||||
pause("Press [Enter] to continue...")
|
||||
|
||||
info()
|
||||
pause("Press [Enter] to continue...")
|
||||
|
||||
demo()
|
||||
pause("Press [Enter] to continue...")
|
||||
|
||||
entropy_file = ask_file()
|
||||
|
||||
try:
|
||||
nsec, npub, priv_hex, pub_hex = generate_key_from_file(entropy_file)
|
||||
except Exception as e:
|
||||
print(f"\n❌ Failed to generate keys: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
show_keys(nsec, npub, priv_hex, pub_hex)
|
||||
pause("Press [Enter] to clear the screen and exit...")
|
||||
|
||||
clear_terminal()
|
||||
print("🚀 Done. Goodbye!\n")
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# If the script is run directly, invoke main()
|
||||
main()
|
||||
@@ -1,125 +0,0 @@
|
||||
Metadata-Version: 2.4
|
||||
Name: nostr-keygen
|
||||
Version: 0.1.0
|
||||
Summary: Terminal tool to generate nostr npub/nsec from file entropy
|
||||
Author-email: Your Name <you@example.com>
|
||||
Requires-Python: >=3.8
|
||||
Description-Content-Type: text/markdown
|
||||
Requires-Dist: ecdsa
|
||||
Requires-Dist: bech32
|
||||
|
||||
# nostr-keygen
|
||||
|
||||
A tiny command‑line utility written in Python that generates an Nostr **npub** (public key) and **nsec** (private key) pair from a single file that you drop into the terminal.
|
||||
|
||||
> **NOTE:** The instructions below work on **macOS** and **Linux**. If you’re on Windows you’ll need a compatible terminal (e.g. WSL, Git‑Bash, or PowerShell with Python).
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Run
|
||||
|
||||
```bash
|
||||
# Drop any file into the terminal prompt!
|
||||
# macOS (most terminals) and many Linux terminals allow drag‑and‑drop.
|
||||
|
||||
nostr-keygen <filepath>
|
||||
```
|
||||
|
||||
> The terminal prompt you see (``$`` on macOS, ``username@host:~$`` on Linux) accepts drag‑and‑drop of any file. The script will read that file path and produce key strings.
|
||||
|
||||
---
|
||||
|
||||
### 📄 Quick test
|
||||
|
||||
```bash
|
||||
# Create a tiny dummy file
|
||||
printf "random entropy" > /tmp/dummy.bin
|
||||
|
||||
# Run the program
|
||||
nostr-keygen /tmp/dummy.bin
|
||||
```
|
||||
|
||||
You should see two lines outputted: an `nsec` string and an `npub` string.
|
||||
|
||||
## 🛠️ Installation
|
||||
|
||||
### 1️⃣ Install Python
|
||||
|
||||
- **macOS**: Use Homebrew
|
||||
```bash
|
||||
brew install python@3.12
|
||||
```
|
||||
(If you already have Python, skip this step.)
|
||||
|
||||
- **Linux (Ubuntu / Debian‑based)**:
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install python3 python3-venv
|
||||
```
|
||||
(Other distros may use `yum`, `dnf`, or your package manager of choice.)
|
||||
|
||||
- **Linux (Arch)**:
|
||||
```bash
|
||||
sudo pacman -S python
|
||||
```
|
||||
|
||||
> **Tip** – On macOS the `python3` binary is typically symlinked to `python`, but on many Linux systems you’ll need `python3` explicitly.
|
||||
|
||||
### 2️⃣ Clone the repo
|
||||
|
||||
```bash
|
||||
git clone https://github.com/your‑github‑handle/nostr-keygen.git
|
||||
cd nostr-keygen
|
||||
```
|
||||
|
||||
### 3️⃣ Create a virtual‑environment (recommended)
|
||||
|
||||
```bash
|
||||
python3 -m venv .venv # create a venv in the repo directory
|
||||
source .venv/bin/activate # activate it (both on macOS and Linux)
|
||||
```
|
||||
|
||||
> The virtual‑environment isolates third‑party libraries (`ecdsa`, `bech32`, etc.) from the system‑wide Python installation, ensuring that installing or updating them won’t accidentally break other projects. It also guarantees that anyone who checks out the repo can recreate the exact same runtime environment.
|
||||
|
||||
### 4️⃣ Install the tool in editable mode (development) or normally
|
||||
|
||||
- **Editable (work on the code as you edit it)**
|
||||
```bash
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
- **Normal installation**
|
||||
```bash
|
||||
pip install .
|
||||
```
|
||||
|
||||
Both forms install the console‑script `nostr‑keygen` into `./.venv/bin`.
|
||||
|
||||
## 🚫 Key‑string safety
|
||||
|
||||
> Keep your `nsec` secret in a secure, offline location. Anyone with that string can sign Nostr events or spend Nostr‑based funds.
|
||||
|
||||
|
||||
## 🔧 How the code works (quick dive)
|
||||
|
||||
```python
|
||||
# main.py
|
||||
import argparse, hashlib
|
||||
from ecdsa import SigningKey, SECP256k1
|
||||
from bech32 import bech32_encode, convertbits
|
||||
|
||||
NSEC_PREFIX, NPUB_PREFIX = "nsec", "npub"
|
||||
|
||||
def _to_bech32(data: bytes, hrp: str) -> str:
|
||||
five_bits = convertbits(list(data), 8, 5, True)
|
||||
return bech32_encode(hrp, five_bits)
|
||||
|
||||
# … (rest unchanged) …
|
||||
```
|
||||
|
||||
### Why the key‑gen algorithm matters
|
||||
|
||||
1. **Read file in binary** – We need raw entropy; reading as text would truncate or encode the file in an unexpected way. Binary mode is the most faithful representation of the file’s content.
|
||||
2. **SHA‑256 hash** – Provides a *deterministic* 32‑byte seed from any file. Different inputs yield different seeds, and the same input always yields the same seed.
|
||||
3. **Create a secp256k1 signing key** – The curve used by Nostr (and Bitcoin) for ECDSA. The secret key is derived directly from the seed bytes.
|
||||
4. **Bech32 encoding** – Nostr keys are human‑readable Bech32 strings prefixed with `nsec` or `npub`. `_to_bech32` converts 8‑bit byte streams to 5‑bit groups and encodes them.
|
||||
@@ -1,9 +0,0 @@
|
||||
README.md
|
||||
main.py
|
||||
pyproject.toml
|
||||
nostr_keygen.egg-info/PKG-INFO
|
||||
nostr_keygen.egg-info/SOURCES.txt
|
||||
nostr_keygen.egg-info/dependency_links.txt
|
||||
nostr_keygen.egg-info/entry_points.txt
|
||||
nostr_keygen.egg-info/requires.txt
|
||||
nostr_keygen.egg-info/top_level.txt
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
[console_scripts]
|
||||
nostr-keygen = main:main
|
||||
@@ -1,2 +0,0 @@
|
||||
ecdsa
|
||||
bech32
|
||||
@@ -1 +0,0 @@
|
||||
main
|
||||
@@ -1,15 +0,0 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=61.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "nostr-keygen"
|
||||
version = "0.1.0"
|
||||
description = "Terminal tool to generate nostr npub/nsec from file entropy"
|
||||
authors = [{name = "Your Name", email = "you@example.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.8"
|
||||
dependencies = ["ecdsa", "bech32"]
|
||||
|
||||
[project.scripts]
|
||||
nostr-keygen = "main:main"
|
||||
Reference in New Issue
Block a user