nostr-nsec-seedphrase
Version:
A comprehensive TypeScript library for Nostr key management with BIP39 seed phrases, supporting both ESM and CommonJS. Implements NIP-01, NIP-06, NIP-19, and NIP-26 with key generation, event signing, bech32 encoding/decoding, and secure cryptographic ope
153 lines (152 loc) • 4.87 kB
JavaScript
/**
* @module crypto/events
* @description Event signing and verification functions for Nostr
*/
import { schnorr } from "@noble/curves/secp256k1.js";
import { sha256 } from "@noble/hashes/sha2.js";
import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
import { logger } from "../utils/logger.js";
import { Defaults } from "../constants.js";
/**
* Calculates the event hash/ID according to the Nostr protocol
* @param event - The event to hash
* @returns The hex-encoded event hash
*/
export function getEventHash(event) {
try {
const serialized = JSON.stringify([
0,
event.pubkey,
event.created_at,
event.kind,
event.tags,
event.content,
]);
return bytesToHex(sha256(new TextEncoder().encode(serialized)));
}
catch (error) {
logger.error("Failed to calculate event hash:", error?.toString());
throw error;
}
}
/**
* Creates an unsigned event
* @param pubkey - The public key of the event creator
* @param content - The event content
* @param kind - The event kind (defaults to TEXT_NOTE)
* @param tags - The event tags (defaults to empty array)
* @returns An unsigned event
*/
export function createUnsignedEvent(pubkey, content, kind = Defaults.KIND, tags = Defaults.TAGS) {
return {
pubkey,
created_at: Defaults.CREATED_AT(),
kind,
tags,
content,
};
}
/**
* Signs a Nostr event using Schnorr signatures
* @param event - The event to sign
* @param privateKey - The hex-encoded private key to sign with
* @returns The signed event
* @throws {Error} If signing fails
*/
export async function signEvent(event, privateKey) {
try {
const id = getEventHash(event);
const sig = bytesToHex(await schnorr.sign(hexToBytes(id), hexToBytes(privateKey)));
logger.log("Event signed successfully");
return {
...event,
id,
sig,
};
}
catch (error) {
logger.error("Failed to sign event:", error?.toString());
throw error;
}
}
/**
* Verifies a Nostr event signature
* @param event - The event to verify
* @returns Validation result
*/
export async function verifyEvent(event) {
try {
if (!event.id || !event.pubkey || !event.sig) {
return {
isValid: false,
error: "Missing required fields",
};
}
const hash = getEventHash(event);
if (hash !== event.id) {
return {
isValid: false,
error: "Event hash mismatch",
};
}
logger.log("Verifying event signature");
const isValid = await schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey));
return {
isValid,
error: isValid ? undefined : "Invalid signature",
};
}
catch (error) {
logger.error("Failed to verify event:", error?.toString());
return {
isValid: false,
error: error instanceof Error ? error.message : "Unknown error",
};
}
}
/**
* Signs a message with a private key using Schnorr signatures
* @param message - The message to sign
* @param privateKey - The hex-encoded private key to sign with
* @returns The hex-encoded signature
* @throws {Error} If signing fails
*/
export async function signMessage(message, privateKey) {
try {
const messageBytes = new TextEncoder().encode(message);
const messageHash = sha256(messageBytes);
const signature = await schnorr.sign(messageHash, hexToBytes(privateKey));
logger.log("Message signed successfully");
return bytesToHex(signature);
}
catch (error) {
logger.error("Failed to sign message:", error?.toString());
throw error;
}
}
/**
* Verifies a message signature using Schnorr verification
* @param message - The original message
* @param signature - The hex-encoded signature to verify
* @param publicKey - The hex-encoded public key to verify against
* @returns Validation result
*/
export async function verifySignature(message, signature, publicKey) {
try {
const messageBytes = new TextEncoder().encode(message);
const messageHash = sha256(messageBytes);
logger.log("Verifying message signature");
const isValid = await schnorr.verify(hexToBytes(signature), messageHash, hexToBytes(publicKey));
return {
isValid,
error: isValid ? undefined : "Invalid signature",
};
}
catch (error) {
logger.error("Failed to verify signature:", error?.toString());
return {
isValid: false,
error: error instanceof Error ? error.message : "Unknown error",
};
}
}