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

117 lines (116 loc) 4.29 kB
/** * @module nips/nip-26 * @description NIP-26 Delegated Event Signing implementation * @see https://github.com/nostr-protocol/nips/blob/master/26.md */ 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"; /** * Creates a delegation token string * @param conditions - Delegation conditions * @returns Delegation token string */ function createDelegationString(delegator, delegatee, conditions) { const parts = ["nostr", "delegation", delegator, delegatee]; if (conditions.kinds) { parts.push(`kinds=${conditions.kinds.join(",")}`); } if (conditions.since) { parts.push(`created_at>${conditions.since}`); } if (conditions.until) { parts.push(`created_at<${conditions.until}`); } return parts.join(":"); } /** * Creates a delegation token * @param delegatee - Public key of the delegatee * @param conditions - Delegation conditions * @param delegatorPrivateKey - Private key of the delegator * @returns Delegation token */ export async function createDelegation(delegatee, conditions, delegatorPrivateKey) { try { // Get delegator's public key const delegator = bytesToHex(schnorr.getPublicKey(hexToBytes(delegatorPrivateKey))); // Create delegation string const tokenString = createDelegationString(delegator, delegatee, conditions); // Sign the token const messageHash = sha256(new TextEncoder().encode(tokenString)); const signature = await schnorr.sign(messageHash, hexToBytes(delegatorPrivateKey)); logger.log("Created delegation token"); return { delegator, delegatee, conditions, signature: bytesToHex(signature), }; } catch (error) { logger.error("Failed to create delegation token:", error?.toString()); throw error; } } /** * Verifies a delegation token * @param token - The delegation token to verify * @param now - Current Unix timestamp in seconds (optional, defaults to current time) * @returns Result of the validation */ async function verifyDelegation(token, now) { try { const tokenObject = JSON.parse(token); // Check conditions if (now) { if (tokenObject.conditions.since && now < tokenObject.conditions.since) { return { isValid: false, error: "Event timestamp before delegation validity period", }; } if (tokenObject.conditions.until && now > tokenObject.conditions.until) { return { isValid: false, error: "Event timestamp after delegation validity period", }; } } // Verify signature const tokenString = createDelegationString(tokenObject.delegator, tokenObject.delegatee, tokenObject.conditions); const messageHash = sha256(new TextEncoder().encode(tokenString)); const isValid = await schnorr.verify(hexToBytes(tokenObject.signature), messageHash, hexToBytes(tokenObject.delegator)); return { isValid, error: isValid ? undefined : "Invalid delegation signature", }; } catch (error) { logger.error("Failed to verify delegation:", error?.toString()); return { isValid: false, error: error instanceof Error ? error.message : "Unknown error", }; } } /** * Checks if a delegation token is currently valid * @param token - The delegation token to check * @param now - Current Unix timestamp in seconds (optional, defaults to current time) * @returns True if the delegation is valid */ export async function isValidDelegation(token, now) { const result = await verifyDelegation(token, now); return result.isValid; } /** * Gets the expiration time of a delegation token * @param token - Delegation token * @returns Expiration timestamp or undefined if no expiration */ export function getDelegationExpiry(token) { const tokenObject = JSON.parse(token); return tokenObject.conditions.until; }