Nostr Python
Executable
Jan 31, 2026
Generate Nostr Keypairs
Create Nostr identity keypairs using secp256k1 cryptography
#keys
#identity
#secp256k1
#bech32
Overview
Generate Nostr identity keypairs. Your private key (nsec) is your identity - guard it carefully. Your public key (npub) is shareable and identifies you on the network.
The Code
"""
Nostr Keypair Generator
Create identity keypairs using secp256k1
Requirements: None (uses hashlib for CSPRNG, includes bech32 encoder)
SECURITY: Private keys grant full control of your Nostr identity.
Never share, log, or expose nsec keys.
"""
import hashlib
import secrets
from typing import Tuple
# Bech32 charset for NIP-19 encoding
BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
# secp256k1 curve parameters
P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
G = (
0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
)
def modinv(a: int, m: int) -> int:
"""Modular multiplicative inverse using extended Euclidean algorithm."""
if a < 0:
a = a % m
g, x, _ = extended_gcd(a, m)
if g != 1:
raise ValueError("Modular inverse does not exist")
return x % m
def extended_gcd(a: int, b: int) -> Tuple[int, int, int]:
"""Extended Euclidean algorithm."""
if a == 0:
return b, 0, 1
g, x, y = extended_gcd(b % a, a)
return g, y - (b // a) * x, x
def point_add(p1: Tuple[int, int], p2: Tuple[int, int]) -> Tuple[int, int]:
"""Add two points on secp256k1."""
if p1 is None:
return p2
if p2 is None:
return p1
x1, y1 = p1
x2, y2 = p2
if x1 == x2 and y1 != y2:
return None
if x1 == x2:
m = (3 * x1 * x1) * modinv(2 * y1, P) % P
else:
m = (y2 - y1) * modinv(x2 - x1, P) % P
x3 = (m * m - x1 - x2) % P
y3 = (m * (x1 - x3) - y1) % P
return (x3, y3)
def scalar_mult(k: int, point: Tuple[int, int]) -> Tuple[int, int]:
"""Multiply a point by a scalar."""
result = None
addend = point
while k:
if k & 1:
result = point_add(result, addend)
addend = point_add(addend, addend)
k >>= 1
return result
def generate_private_key() -> bytes:
"""
Generate a cryptographically secure private key.
Returns:
32 bytes private key
"""
while True:
key = secrets.token_bytes(32)
key_int = int.from_bytes(key, 'big')
# Ensure key is valid for secp256k1
if 0 < key_int < N:
return key
def private_to_public(private_key: bytes) -> bytes:
"""
Derive public key from private key.
Args:
private_key: 32 bytes private key
Returns:
32 bytes public key (x-coordinate only, for Nostr)
"""
k = int.from_bytes(private_key, 'big')
point = scalar_mult(k, G)
return point[0].to_bytes(32, 'big')
def bech32_polymod(values: list) -> int:
"""Bech32 checksum computation."""
generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
chk = 1
for value in values:
top = chk >> 25
chk = (chk & 0x1ffffff) << 5 ^ value
for i in range(5):
chk ^= generator[i] if ((top >> i) & 1) else 0
return chk
def bech32_hrp_expand(hrp: str) -> list:
"""Expand HRP for checksum."""
return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]
def bech32_create_checksum(hrp: str, data: list) -> list:
"""Create bech32 checksum."""
values = bech32_hrp_expand(hrp) + data
polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1
return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
def convertbits(data: bytes, frombits: int, tobits: int, pad: bool = True) -> list:
"""Convert between bit sizes."""
acc = 0
bits = 0
ret = []
maxv = (1 << tobits) - 1
for value in data:
acc = (acc << frombits) | value
bits += frombits
while bits >= tobits:
bits -= tobits
ret.append((acc >> bits) & maxv)
if pad and bits:
ret.append((acc << (tobits - bits)) & maxv)
return ret
def bytes_to_bech32(hrp: str, data: bytes) -> str:
"""
Encode bytes to bech32 string.
Args:
hrp: Human-readable prefix ('nsec' or 'npub')
data: 32 bytes to encode
Returns:
bech32-encoded string
"""
data5bit = convertbits(data, 8, 5)
checksum = bech32_create_checksum(hrp, data5bit)
return hrp + '1' + ''.join(BECH32_CHARSET[d] for d in data5bit + checksum)
def generate_keypair() -> dict:
"""
Generate a complete Nostr keypair.
Returns:
dict with private key, public key, nsec, and npub
"""
private_key = generate_private_key()
public_key = private_to_public(private_key)
return {
"private_key_hex": private_key.hex(),
"public_key_hex": public_key.hex(),
"nsec": bytes_to_bech32("nsec", private_key),
"npub": bytes_to_bech32("npub", public_key)
}
def validate_keypair(nsec: str, npub: str) -> bool:
"""
Validate that nsec and npub form a valid keypair.
Returns:
True if valid keypair
"""
# This would require bech32 decoding
# Simplified: just check format
return (
nsec.startswith("nsec1") and
npub.startswith("npub1") and
len(nsec) == 63 and
len(npub) == 63
)
# Example usage
if __name__ == "__main__":
print("=== Nostr Keypair Generator ===\n")
keypair = generate_keypair()
print("PUBLIC KEY (shareable):")
print(f" npub: {keypair['npub']}")
print(f" hex: {keypair['public_key_hex']}")
print("\nPRIVATE KEY (SECRET - DO NOT SHARE):")
print(f" nsec: {keypair['nsec']}")
print(f" hex: {keypair['private_key_hex']}")
print("\n" + "=" * 50)
print("SECURITY WARNINGS:")
print("1. NEVER share your nsec (private key)")
print("2. NEVER paste nsec into websites")
print("3. Back up nsec securely (password manager)")
print("4. Anyone with nsec can impersonate you")
print("=" * 50)
# Validate the keypair
is_valid = validate_keypair(keypair['nsec'], keypair['npub'])
print(f"\nKeypair valid: {is_valid}")
Usage
# No external dependencies required
python generate_keys.py
Example Output
=== Nostr Keypair Generator ===
PUBLIC KEY (shareable):
npub: npub1qy6nkwjz69...
hex: 012a5b38298f...
PRIVATE KEY (SECRET - DO NOT SHARE):
nsec: nsec1xyz789abc...
hex: 3456def789...
==================================================
SECURITY WARNINGS:
1. NEVER share your nsec (private key)
2. NEVER paste nsec into websites
3. Back up nsec securely (password manager)
4. Anyone with nsec can impersonate you
==================================================
Keypair valid: True
Agent Notes
Key formats:
npub1...: Public key in bech32 (NIP-19)nsec1...: Private key in bech32 (NIP-19)- Hex: Raw 32-byte keys as hexadecimal
Security for agents:
- Store nsec in secure environment variables
- Never log private keys
- Use separate keys for different agent identities
- Consider key rotation for high-risk operations
For production: Use established libraries like pynostr or nostr-sdk which have been audited.