neco-secp 0.1.0

Minimal secp256k1 and Nostr signing core
Documentation

neco-secp

Minimal secp256k1 and Nostr signing core, for building Nostr clients and tools in pure Rust.

This crate provides a small pure Rust core for secret keys, x-only public keys, Schnorr signatures, and Nostr event signing. Storage, browser integration, and key isolation policy are handled by adjacent higher-level crates.

Features

  • nip19: enable NIP-19 bare entities (npub, nsec, note) and TLV entities (nprofile, nevent, naddr, nrelay)
  • nip04: enable NIP-04 encryption/decryption helpers
  • nip44: enable NIP-44 v2 conversation key, encryption, and decryption helpers
  • serde: derive serialization for event structs
  • nostr: enable Nostr event helpers and signed-event JSON support
  • batch: enable batch key generation and mining helpers

Usage

use neco_secp::{nostr, SecretKey, UnsignedEvent};

let secret = SecretKey::generate().unwrap();
let event = UnsignedEvent {
    created_at: 1_700_000_000,
    kind: 1,
    tags: vec![],
    content: "hello".to_string(),
};

let signed = nostr::finalize_event(event, &secret).unwrap();
nostr::verify_event(&signed).unwrap();

KeyBundle bundles a secret key and its x-only public key together.

use neco_secp::KeyBundle;

let bundle = KeyBundle::generate().unwrap();
let secret = bundle.secret();
let xonly = bundle.xonly_public_key();
# let _ = (secret, xonly);

With nip19 feature:

use neco_secp::{mine_pow, KeyBundle};

let bundle = KeyBundle::generate().unwrap();
let npub = bundle.npub().unwrap();
let nsec = bundle.nsec().unwrap();

batch feature enables generating multiple key pairs at once, and mining helpers.

use neco_secp::KeyBundle;

// batch key generation
let bundles = KeyBundle::generate_batch(100).unwrap();

// PoW mining: find a key with N leading zero hex nibbles in the xonly pubkey
let bundle = mine_pow(4, 1_000_000).unwrap();

Vanity mining requires both batch and nip19 features.

use neco_secp::mine_vanity_npub;

// find a key whose npub starts with the given prefix
let bundle = mine_vanity_npub("npub1abc", 1_000_000).unwrap();

NIP-19 entities are available behind the nip19 feature.

use neco_secp::{nip19, NProfile, SecretKey};

let secret = SecretKey::generate().unwrap();
let nsec = nip19::encode_nsec(&secret).unwrap();
let decoded = nip19::decode(&nsec).unwrap();

assert!(matches!(decoded, neco_secp::Nip19::Nsec(_)));

let profile = NProfile {
    pubkey: secret.xonly_public_key().unwrap(),
    relays: vec!["wss://relay.example".to_string()],
};
let nprofile = nip19::encode_nprofile(&profile).unwrap();
let decoded = nip19::decode(&nprofile).unwrap();

assert!(matches!(decoded, neco_secp::Nip19::NProfile(_)));

NIP-44 helpers are available behind the nip44 feature.

use neco_secp::{nip44, SecretKey};

let secret = SecretKey::from_bytes([0x11; 32]).unwrap();
let peer = SecretKey::from_bytes([0x22; 32]).unwrap();
let peer_pubkey = peer.xonly_public_key().unwrap();
let conversation_key = nip44::get_conversation_key(&secret, &peer_pubkey).unwrap();
let payload = nip44::encrypt("hello", &conversation_key, Some([0x33; 32])).unwrap();
let plaintext = nip44::decrypt(&payload, &conversation_key).unwrap();

assert_eq!(plaintext, "hello");

NIP-04 helpers are available behind the nip04 feature.

use neco_secp::{nip04, SecretKey};

let secret = SecretKey::from_bytes([0x11; 32]).unwrap();
let peer = SecretKey::from_bytes([0x22; 32]).unwrap();
let peer_pubkey = peer.xonly_public_key().unwrap();
let payload = nip04::encrypt(&secret, &peer_pubkey, "hello", Some([0x44; 16])).unwrap();
let plaintext = nip04::decrypt(&peer, &secret.xonly_public_key().unwrap(), &payload).unwrap();

assert_eq!(plaintext, "hello");

Testing

Normal cargo test runs don't include expensive mining checks.

  • Vanity mining stress tests run only when AINE_RUN_VANITY_TESTS=1 is set.
  • PoW mining stress tests run only when AINE_RUN_POW_TESTS=1 is set.

Examples:

# run ordinary tests
cargo test -p neco-secp

# include expensive PoW mining checks
AINE_RUN_POW_TESTS=1 cargo test -p neco-secp --features batch

# include expensive vanity mining checks
AINE_RUN_VANITY_TESTS=1 cargo test -p neco-secp --features "batch nip19"

API

Item Description
SecretKey Validated 32-byte secp256k1 secret key
XOnlyPublicKey Validated 32-byte x-only public key
SchnorrSignature 64-byte Schnorr signature
KeyBundle Bundled secret key and x-only public key pair
KeyBundle::generate() Generates a random key pair
KeyBundle::secret() / xonly_public_key() Borrow the validated secret key and x-only public key
KeyBundle::npub() Returns the npub string (nip19)
KeyBundle::nsec() Returns the nsec string (nip19)
KeyBundle::generate_batch(n) Generates n key pairs (batch)
mine_pow(d, n) Finds a key with d leading zero hex nibbles (batch)
mine_vanity_npub(p, n) Finds a key whose npub starts with p (batch + nip19)
UnsignedEvent Unsigned Nostr event payload
SignedEvent Signed Nostr event
nostr Event serialization, signing, and verification helpers
nip19 Bare/TLV NIP-19 encode/decode helpers
nip04 NIP-04 payload helpers
nip44 NIP-44 v2 conversation key and payload helpers

License

MIT