Initial commit: basic keygen tool
This commit is contained in:
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
__pycache__/
|
||||||
|
venv/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
|
||||||
|
# OS generated files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
47
README.md
Normal file
47
README.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# README.md
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
1. **Entropy** – The contents of the file you provide are read in binary mode.
|
||||||
|
2. **Hash** – The data is hashed with SHA‑256 to produce a 32‑byte seed.
|
||||||
|
3. **Key generation** – The seed is fed to the secp256k1 curve to create an ECDSA private key.
|
||||||
|
4. **Bech32 encoding** – The private key is encoded as `nsec`; the compressed public key is encoded as `npub`.
|
||||||
|
|
||||||
|
The utility is intentionally lightweight; it has no external configuration and works on any platform with Python 3.8+.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone the repo
|
||||||
|
git clone https://github.com/yourname/nostr-keygen.git
|
||||||
|
cd nostr-keygen
|
||||||
|
|
||||||
|
# Create a virtualenv and install deps
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
pip install -e . # installs the program and its deps
|
||||||
|
```
|
||||||
|
|
||||||
|
After that you can run it with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Replace file.txt with the path to any file you want to use as entropy
|
||||||
|
nostr-keygen file.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using drag‑and‑drop in the terminal
|
||||||
|
|
||||||
|
On macOS (and most X11 terminals) you can simply drag a file into the terminal prompt. The terminal translates that to the file’s full path. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ nostr-keygen <dragged file>
|
||||||
|
```
|
||||||
|
|
||||||
|
This will invoke the program using the path of the dropped file.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
79
main.py
Normal file
79
main.py
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#!/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("NPop:", npub)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
12
pyproject.toml
Normal file
12
pyproject.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[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"]
|
||||||
Reference in New Issue
Block a user