UNPKG

nostr-websocket-utils

Version:

Robust WebSocket utilities for Nostr applications with automatic reconnection, supporting both ESM and CommonJS. Features channel-based messaging, heartbeat monitoring, message queueing, and comprehensive error handling with type-safe handlers.

134 lines 5.43 kB
/** * @file NIP-44: Versioned Encrypted Payloads * @module nips/nip-44 * @see https://github.com/nostr-protocol/nips/blob/master/44.md * * NIP-44 replaces NIP-04 with a modern encryption scheme using * ChaCha20 + HMAC-SHA256. This module provides DM-level helpers * that parallel the NIP-04 module (nip-04.ts). */ import { nip44, hexToBytes, getPublicKeySync } from 'nostr-crypto-utils'; /** * Kind value for NIP-44 encrypted direct messages (gift-wrapped DMs use kind 14, * but for parity with NIP-04 kind 4 usage, callers may choose their own kind). * NIP-44 itself is a payload format, not a kind — the kind depends on the use case. * We default to kind 44 as a convenience constant; callers should override as needed. */ export const ENCRYPTED_DM_KIND_44 = 44; /** * Creates a NIP-44 conversation key from a sender's private key and recipient's public key. * This key is symmetric and reusable for all messages in the conversation. * @param senderPrivkeyHex - Sender's private key in hex * @param recipientPubkeyHex - Recipient's public key in hex * @returns Conversation key as Uint8Array */ export function getConversationKey(senderPrivkeyHex, recipientPubkeyHex) { const privkeyBytes = hexToBytes(senderPrivkeyHex); return nip44.getConversationKey(privkeyBytes, recipientPubkeyHex); } /** * Encrypts a message using NIP-44 * @param plaintext - Message content to encrypt * @param senderPrivkeyHex - Sender's private key in hex * @param recipientPubkeyHex - Recipient's public key in hex * @returns Encrypted payload string (base64) */ export function encryptNip44(plaintext, senderPrivkeyHex, recipientPubkeyHex) { const conversationKey = getConversationKey(senderPrivkeyHex, recipientPubkeyHex); return nip44.encrypt(plaintext, conversationKey); } /** * Decrypts a NIP-44 encrypted payload * @param payload - Encrypted payload string (base64) * @param recipientPrivkeyHex - Recipient's private key in hex * @param senderPubkeyHex - Sender's public key in hex * @returns Decrypted plaintext */ export function decryptNip44(payload, recipientPrivkeyHex, senderPubkeyHex) { const conversationKey = getConversationKey(recipientPrivkeyHex, senderPubkeyHex); return nip44.decrypt(payload, conversationKey); } /** * Creates an encrypted direct message event using NIP-44 * @param content - Message content to encrypt * @param recipientPubkey - Recipient's public key (hex) * @param senderPrivkey - Sender's private key (hex) * @param tags - Additional tags for the event * @param kind - Event kind (defaults to ENCRYPTED_DM_KIND_44) * @returns Encrypted message event as NostrWSMessage */ export function createEncryptedDM44(content, recipientPubkey, senderPrivkey, tags = [], kind = ENCRYPTED_DM_KIND_44) { try { const encryptedContent = encryptNip44(content, senderPrivkey, recipientPubkey); const senderPubkey = getPublicKeySync(senderPrivkey); return ['EVENT', { kind, pubkey: senderPubkey, content: encryptedContent, tags: [ ['p', recipientPubkey], ...tags ] }]; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; throw new Error(`Failed to create NIP-44 encrypted DM: ${errorMessage}`); } } /** * Decrypts a received direct message event encrypted with NIP-44 * @param message - Received message * @param recipientPrivkey - Recipient's private key (hex) * @param senderPubkey - Sender's public key (hex) * @param logger - Logger instance * @returns Decrypted message content */ export function decryptDM44(message, recipientPrivkey, senderPubkey, logger) { try { if (!Array.isArray(message) || message[0] !== 'EVENT') { throw new Error('Invalid message format'); } const event = message[1]; return decryptNip44(event.content, recipientPrivkey, senderPubkey); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; logger.error({ error: errorMessage }, 'Failed to decrypt NIP-44 DM'); throw new Error(`Failed to decrypt NIP-44 DM: ${errorMessage}`); } } /** * Validates a NIP-44 encrypted DM event format * @param message - Message to validate * @param logger - Logger instance * @returns True if message follows NIP-44 encrypted DM format */ export function validateEncryptedDM44(message, logger) { try { if (!Array.isArray(message) || message[0] !== 'EVENT') { logger.debug('Invalid message format'); return false; } const event = message[1]; if (!event.content || typeof event.content !== 'string') { logger.debug('Missing or invalid content'); return false; } if (!Array.isArray(event.tags)) { logger.debug('Missing tags array'); return false; } const recipientTag = event.tags.find((tag) => Array.isArray(tag) && tag[0] === 'p' && tag[1]); if (!recipientTag) { logger.debug('Missing recipient tag'); return false; } return true; } catch (error) { logger.error({ error }, 'Error validating NIP-44 encrypted DM'); return false; } } //# sourceMappingURL=nip-44.js.map