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
161 lines (160 loc) • 5.58 kB
JavaScript
;
/**
* @module crypto/events
* @description Event signing and verification functions for Nostr
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getEventHash = getEventHash;
exports.createUnsignedEvent = createUnsignedEvent;
exports.signEvent = signEvent;
exports.verifyEvent = verifyEvent;
exports.signMessage = signMessage;
exports.verifySignature = verifySignature;
const secp256k1_js_1 = require("@noble/curves/secp256k1.js");
const sha2_js_1 = require("@noble/hashes/sha2.js");
const utils_js_1 = require("@noble/hashes/utils.js");
const logger_js_1 = require("../utils/logger.js");
const constants_js_1 = require("../constants.js");
/**
* Calculates the event hash/ID according to the Nostr protocol
* @param event - The event to hash
* @returns The hex-encoded event hash
*/
function getEventHash(event) {
try {
const serialized = JSON.stringify([
0,
event.pubkey,
event.created_at,
event.kind,
event.tags,
event.content,
]);
return (0, utils_js_1.bytesToHex)((0, sha2_js_1.sha256)(new TextEncoder().encode(serialized)));
}
catch (error) {
logger_js_1.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
*/
function createUnsignedEvent(pubkey, content, kind = constants_js_1.Defaults.KIND, tags = constants_js_1.Defaults.TAGS) {
return {
pubkey,
created_at: constants_js_1.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
*/
async function signEvent(event, privateKey) {
try {
const id = getEventHash(event);
const sig = (0, utils_js_1.bytesToHex)(await secp256k1_js_1.schnorr.sign((0, utils_js_1.hexToBytes)(id), (0, utils_js_1.hexToBytes)(privateKey)));
logger_js_1.logger.log("Event signed successfully");
return {
...event,
id,
sig,
};
}
catch (error) {
logger_js_1.logger.error("Failed to sign event:", error?.toString());
throw error;
}
}
/**
* Verifies a Nostr event signature
* @param event - The event to verify
* @returns Validation result
*/
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_js_1.logger.log("Verifying event signature");
const isValid = await 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));
return {
isValid,
error: isValid ? undefined : "Invalid signature",
};
}
catch (error) {
logger_js_1.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
*/
async function signMessage(message, privateKey) {
try {
const messageBytes = new TextEncoder().encode(message);
const messageHash = (0, sha2_js_1.sha256)(messageBytes);
const signature = await secp256k1_js_1.schnorr.sign(messageHash, (0, utils_js_1.hexToBytes)(privateKey));
logger_js_1.logger.log("Message signed successfully");
return (0, utils_js_1.bytesToHex)(signature);
}
catch (error) {
logger_js_1.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
*/
async function verifySignature(message, signature, publicKey) {
try {
const messageBytes = new TextEncoder().encode(message);
const messageHash = (0, sha2_js_1.sha256)(messageBytes);
logger_js_1.logger.log("Verifying message signature");
const isValid = await secp256k1_js_1.schnorr.verify((0, utils_js_1.hexToBytes)(signature), messageHash, (0, utils_js_1.hexToBytes)(publicKey));
return {
isValid,
error: isValid ? undefined : "Invalid signature",
};
}
catch (error) {
logger_js_1.logger.error("Failed to verify signature:", error?.toString());
return {
isValid: false,
error: error instanceof Error ? error.message : "Unknown error",
};
}
}