UNPKG

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

231 lines (230 loc) 8.79 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.sign = sign; exports.verify = verify; exports.verifyWithResult = verifyWithResult; exports.getSharedSecret = getSharedSecret; exports.hmacSha256Sync = hmacSha256Sync; exports.hmacSha256Async = hmacSha256Async; exports.getEventHash = getEventHash; exports.signEvent = signEvent; exports.verifyEvent = verifyEvent; exports.signMessage = signMessage; exports.verifyMessage = verifyMessage; exports.configureHMAC = configureHMAC; const secp256k1_js_1 = require("@noble/curves/secp256k1.js"); const sha2_js_1 = require("@noble/hashes/sha2.js"); const hmac_js_1 = require("@noble/hashes/hmac.js"); const utils_js_1 = require("@noble/hashes/utils.js"); const logger_js_1 = __importDefault(require("../utils/logger.js")); /** * Signs a message with a private key * @param {string} message - Message to sign * @param {string} privateKey - Private key in hex format * @returns {Promise<string>} Signature in hex format */ async function sign(message, privateKey) { try { const messageBytes = new TextEncoder().encode(message); const messageHash = (0, sha2_js_1.sha256)(messageBytes); const messageHashHex = (0, utils_js_1.bytesToHex)(messageHash); const signature = await secp256k1_js_1.schnorr.sign(messageHash, (0, utils_js_1.hexToBytes)(privateKey)); return (0, utils_js_1.bytesToHex)(signature); } catch (error) { logger_js_1.default.error("Failed to sign message:", error?.toString()); throw new Error("Failed to sign message"); } } /** * Verifies a signature * @param {string} signature - Signature in hex format * @param {string} message - Original message that was signed * @param {string} publicKey - Public key in hex format * @returns {Promise<boolean>} True if signature is valid */ async function verify(signature, message, publicKey) { try { const messageBytes = new TextEncoder().encode(message); const messageHash = (0, sha2_js_1.sha256)(messageBytes); const isValid = await secp256k1_js_1.schnorr.verify((0, utils_js_1.hexToBytes)(signature), messageHash, (0, utils_js_1.hexToBytes)(publicKey)); return isValid; } catch (error) { logger_js_1.default.error("Failed to verify signature:", error?.toString()); return false; } } /** * Verifies a signature and returns a detailed result * @param {string} signature - Signature in hex format * @param {string} message - Original message that was signed * @param {string} publicKey - Public key in hex format * @returns {Promise<ValidationResult>} Validation result with details */ async function verifyWithResult(signature, message, publicKey) { try { const isValid = await verify(signature, message, publicKey); return { isValid, error: isValid ? undefined : "Invalid signature", }; } catch (error) { logger_js_1.default.error("Failed to verify with result:", error?.toString()); return { isValid: false, error: error?.toString() || "Unknown error", }; } } /** * Derives a shared secret using ECDH * @param {string} privateKey - Private key in hex format * @param {string} publicKey - Public key in hex format (32-byte x-only Nostr pubkey) * @returns {Promise<Uint8Array>} Shared secret bytes (x-coordinate of the shared point) */ async function getSharedSecret(privateKey, publicKey) { try { const privKeyBytes = (0, utils_js_1.hexToBytes)(privateKey); // Nostr uses 32-byte x-only pubkeys; prefix with '02' for compressed format const sharedPoint = secp256k1_js_1.secp256k1.getSharedSecret(privKeyBytes, (0, utils_js_1.hexToBytes)("02" + publicKey)); // Extract x-coordinate only (bytes 1..33), per Nostr NIP-04 convention const sharedX = sharedPoint.slice(1, 33); privKeyBytes.fill(0); // zero sensitive material return sharedX; } catch (error) { logger_js_1.default.error("Failed to get shared secret:", error?.toString()); throw new Error("Failed to get shared secret"); } } /** * Synchronously computes HMAC-SHA256 * @param {Uint8Array} key - Key bytes * @param {Uint8Array} message - Message bytes * @returns {Uint8Array} HMAC bytes */ function hmacSha256Sync(key, message) { return hmac_js_1.hmac.create(sha2_js_1.sha256, key).update(message).digest(); } /** * Asynchronously computes HMAC-SHA256 * @param {Uint8Array} key - Key bytes * @param {Uint8Array} message - Message bytes * @returns {Promise<Uint8Array>} HMAC bytes */ async function hmacSha256Async(key, message) { return hmacSha256Sync(key, message); } /** * Calculates the event hash/ID according to the Nostr protocol * @param {UnsignedEvent} event - The unsigned event to hash * @returns {string} The event hash in hex format */ function getEventHash(event) { try { const serialized = JSON.stringify([ 0, event.pubkey, event.created_at, event.kind, event.tags, event.content, ]); const hash = (0, sha2_js_1.sha256)(new TextEncoder().encode(serialized)); return (0, utils_js_1.bytesToHex)(hash); } catch (error) { logger_js_1.default.error("Failed to get event hash:", error?.toString()); throw new Error("Failed to get event hash"); } } /** * Signs an event with a private key * @param {UnsignedEvent} event - The unsigned event to sign * @param {string} privateKey - Private key in hex format * @returns {Promise<string>} The signature in hex format */ async function signEvent(event, privateKey) { try { const eventHash = getEventHash(event); const sig = await secp256k1_js_1.schnorr.sign((0, utils_js_1.hexToBytes)(eventHash), (0, utils_js_1.hexToBytes)(privateKey)); return (0, utils_js_1.bytesToHex)(sig); } catch (error) { logger_js_1.default.error("Failed to sign event:", error?.toString()); throw new Error("Failed to sign event"); } } /** * Verifies an event signature * @param {NostrEvent} event - The signed event to verify * @returns {Promise<boolean>} True if the signature is valid */ async function verifyEvent(event) { try { const hash = getEventHash({ pubkey: event.pubkey, created_at: event.created_at, kind: event.kind, tags: event.tags, content: event.content, }); return secp256k1_js_1.schnorr.verify((0, utils_js_1.hexToBytes)(event.sig), (0, utils_js_1.hexToBytes)(hash), (0, utils_js_1.hexToBytes)(event.pubkey)); } catch (error) { logger_js_1.default.error("Failed to verify event:", error?.toString()); return false; } } /** * Signs a message with a private key * @param {string} message - Message to sign * @param {string} privateKey - Private key in hex format * @returns {Promise<string>} The signature in hex format */ async function signMessage(message, privateKey) { try { const messageHash = (0, sha2_js_1.sha256)(new TextEncoder().encode(message)); const sig = await secp256k1_js_1.schnorr.sign(messageHash, (0, utils_js_1.hexToBytes)(privateKey)); return (0, utils_js_1.bytesToHex)(sig); } catch (error) { logger_js_1.default.error("Failed to sign message:", error?.toString()); throw new Error("Failed to sign message"); } } /** * Verifies a message signature * @param {string} signature - Signature in hex format * @param {string} message - Original message that was signed * @param {string} publicKey - Public key in hex format * @returns {Promise<boolean>} True if the signature is valid */ async function verifyMessage(signature, message, publicKey) { try { const messageHash = (0, sha2_js_1.sha256)(new TextEncoder().encode(message)); return await secp256k1_js_1.schnorr.verify((0, utils_js_1.hexToBytes)(signature), messageHash, (0, utils_js_1.hexToBytes)(publicKey)); } catch (error) { logger_js_1.default.error("Failed to verify message:", error?.toString()); return false; } } /** * Configures the HMAC functions for schnorr */ function configureHMAC() { try { secp256k1_js_1.schnorr.utils.hmacSha256Sync = hmacSha256Sync; secp256k1_js_1.schnorr.utils.hmacSha256Async = hmacSha256Async; } catch (error) { logger_js_1.default.error("Failed to configure HMAC:", error?.toString()); throw new Error("Failed to configure HMAC"); } }