HumanjavaEnterprises/nostr-crypto-utils

GitHub: HumanjavaEnterprises/nostr-crypto-utils

一个面向 Nostr 协议的 TypeScript 加密工具库,提供完整的 NIP 加密规范实现与跨平台运行支持。

Stars: 1 | Forks: 0

# Nostr Crypto Utils 一个用于 Nostr 协议实现的全面加密工具库,专注于核心安全操作和跨平台兼容性。 ## 概述 该库提供了实现 Nostr 协议所需的基本加密操作和实用工具,主要关注: - 用于 Nostr 协议的 Schnorr 签名 - 密钥管理和验证 - 事件签名和验证 - HTTP Auth (NIP-98) - Gift Wrap (NIP-59) 和 私有私信 (NIP-17) - 版本化加密 payload (NIP-44) - 远程签名 / Nostr Connect (NIP-46) - 私钥加密 / ncryptsec (NIP-49) - Bech32 编码的实体 (NIP-19) - 认证协议 (NIP-42) - 加密私信 (NIP-04 — _已弃用_,请使用 NIP-17) - 委托事件签名 (NIP-26 — _已弃用_,请使用 NIP-46) ## 核心功能 ### 加密操作 - 使用 Schnorr 签名进行密钥生成和验证 - 事件签名和验证(兼容 NIP-01) - NIP-04 加密/解密 - 共享密钥计算 - 委托 token 处理 (NIP-26) - 认证协议 (NIP-42) ### 编码实用工具 - Hex 编码/解码 - Base64 编码/解码 - UTF-8 编码/解码 - 二进制数据处理 - Bech32 编码/解码 (NIP-19) ### 协议支持 - NIP-01:基本协议流程 - NIP-17:私有私信 - NIP-19:Bech32 编码的实体 - NIP-42:认证协议 - NIP-44:版本化加密 payload - NIP-46:Nostr Connect (远程签名) - NIP-49:私钥加密 (ncryptsec) - NIP-59:Gift Wrap - NIP-98:HTTP Auth - NIP-04:加密私信 _(已弃用 → NIP-17)_ - NIP-26:委托事件签名 _(已弃用 → NIP-46)_ ## 项目结构 ``` ├── src/ │ ├── crypto.ts # Core cryptographic operations │ ├── encoding/ # Encoding utilities │ │ ├── base64.ts │ │ ├── hex.ts │ │ └── index.ts │ ├── types/ # Type definitions │ │ ├── base.ts │ │ ├── messages.ts │ │ ├── protocol.ts │ │ └── guards.ts │ ├── nips/ # NIP implementations │ └── utils/ # Utility functions ``` ## 技术要求 - **Edge 原生**:无需修改即可在 Cloudflare Workers、Deno、浏览器和 Node.js 18+ 上运行 —— 不需要 Node polyfills - 支持 ESM 和 CJS 格式,并提供针对特定 NIP 的子路径导出(如 `nostr-crypto-utils/nip98` 等) - TypeScript 优先,带有完整的类型定义 - 5 个运行时依赖,全部经过审计且仅用于加密(`@noble/ciphers`、`@noble/curves`、`@noble/hashes`、`@scure/base`、`bech32`) [![npm version](https://badge.fury.io/js/nostr-crypto-utils.svg)](https://www.npmjs.com/package/nostr-crypto-utils) [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/) [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/HumanjavaEnterprises/nostr-crypto-utils/blob/main/LICENSE) [![Documentation](https://img.shields.io/badge/docs-TypeDoc-blue.svg)](https://humanjavaenterprises.github.io/nostr-crypto-utils/) [![Build Status](https://img.shields.io/github/workflow/status/HumanjavaEnterprises/nostr-crypto-utils/CI)](https://github.com/HumanjavaEnterprises/nostr-crypto-utils/actions) [![Coverage Status](https://coveralls.io/repos/github/HumanjavaEnterprises/nostr-crypto-utils/badge.svg?branch=main)](https://coveralls.io/github/HumanjavaEnterprises/nostr-crypto-utils?branch=main) [![Known Vulnerabilities](https://snyk.io/test/github/HumanjavaEnterprises/nostr-crypto-utils/badge.svg)](https://snyk.io/test/github/HumanjavaEnterprises/nostr-crypto-utils) ## 安全提示 ⚠️ **重要**:本库处理的加密密钥和操作对于保护您的 Nostr 身份和数据至关重要。所有加密操作(包括密钥生成、签名和加密)必须采取适当的安全措施进行处理。 如果您发现安全漏洞,请遵循我们的[安全策略](SECURITY.md)并通过 [GitHub 的安全公告功能](https://github.com/humanjavaenterprises/nostr-crypto-utils/security/advisories/new)进行报告。 ### 依赖项漏洞状态 我们会积极监控并解决此代码库中的安全漏洞。**`npm audit --omit=dev` 报告此包的漏洞为零** —— 生产依赖中不存在已知的安全问题。 任何剩余的 `npm audit` 发现都在仅用于开发的工具(eslint、typescript-eslint、vitest 等)中,且源于没有上游修复的传递依赖。这些是 devDependencies,永远不会包含在发布的包中,对本库的使用者没有任何风险。我们会监控上游修复,并在可用时及时更新。 ## 为什么选择 nostr-crypto-utils? - **Edge 原生**:零 Node polyfills —— 可直接部署到 Cloudflare Workers、Deno 和浏览器 - **精简、经过审计的依赖**:仅有 5 个仅用于加密的运行时依赖(`@noble/*`、`@scure/base`、`bech32`)—— 攻击面小,易于审计 - **类型优先**:从零开始使用 TypeScript 构建,确保最大程度的类型安全 - **模块化设计**:针对特定 NIP 的子路径导出 —— 仅导入您所需的内容 - **互补性**:与 nostr-nsec-seedphrase 无缝协作,实现完整的密钥管理 - **专注安全**:严格的验证和全面的测试覆盖(161 个测试) ## 特性 - **完全兼容 NIP**:根据 Nostr 实现可能性 (NIP) 实现所有必需的加密操作 - **类型安全**:提供全面的类型定义的完整 TypeScript 支持 - **事件处理**:创建、签名和验证 Nostr 事件 - **消息格式化**:用于中继通信的符合协议的消息格式化 - **加密**:版本化的 NIP-44 加密、NIP-59 gift wrap、NIP-17 私有私信 - **HTTP Auth**:NIP-98 签名请求认证(事件 + 头部构建/验证,无传输) - **验证**:对事件、过滤器和订阅进行全面验证 - **Edge 原生**:Cloudflare Workers / Deno / 浏览器 / Node.js —— 无需 polyfills - **精简依赖**:5 个经过审计的仅用于加密的运行时依赖 ## 支持的 NIP 本库实现了以下 Nostr 实现可能性 (NIP): | NIP | 标题 | 描述 | 状态 | |-----|-------|-------------|---------| | [NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md) | 基本协议流程描述 | 核心协议功能,包括事件创建、签名和验证 | ✅ 完成 | | [NIP-17](https://github.com/nostr-protocol/nips/blob/master/17.md) | 私有私信 | 基于 NIP-44 + NIP-59 gift wrap 的加密聊天 (`kind 14`) | ✅ 完成 | | [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md) | bech32 编码的实体 | 用于密钥、事件和其他实体的人类可读编码 | ✅ 完成 | | [NIP-42](https://github.com/nostr-protocol/nips/blob/master/42.md) | 认证 | 客户端-中继认证协议 | ✅ 完成 | | [NIP-44](https://github.com/nostr-protocol/nips/blob/master/44.md) | 版本化加密 Payload | 现代加密 (v2: ChaCha20 + HKDF + HMAC) | ✅ 完成 | | [NIP-46](https://github.com/nostr-protocol/nips/blob/master/46.md) | Nostr Connect (远程签名) | 通过 bunker 进行远程签名的协议层 | ✅ 完成 | | [NIP-49](https://github.com/nostr-protocol/nips/blob/master/49.md) | 私钥加密 | ncryptsec 密码加密密钥存储 | ✅ 完成 | | [NIP-59](https://github.com/nostr-protocol/nips/blob/master/59.md) | Gift Wrap | 基于 NIP-44 的 Rumor → seal (`kind 13`) → gift wrap (`kind 1059`/`21059`) | ✅ 完成 | | [NIP-98](https://github.com/nostr-protocol/nips/blob/master/98.md) | HTTP Auth | 构建/验证 `kind 27235` 事件 + `Authorization: Nostr` 头部(不执行 HTTP 请求) | ✅ 完成 | | [NIP-04](https://github.com/nostr-protocol/nips/blob/master/04.md) | 加密私信 | 旧版私信 —— 上游**已弃用**,推荐使用 NIP-17 | ⚠️ 已弃用 | | [NIP-26](https://github.com/nostr-protocol/nips/blob/master/26.md) | 委托事件签名 | 上游**已弃用**,推荐使用 NIP-46 来代表密钥执行操作 | ⚠️ 已弃用 | ### NIP-01 功能 - 事件创建和序列化 - 事件签名和验证 - 事件 ID 计算 - 基本协议验证 ### NIP-04 功能 - 端到端加密私信 - 安全的密钥交换 - 消息加密/解密 ### NIP-19 功能 - Bech32 编码/解码用于: - 公钥 (`npub`) - 私钥 (`nsec`) - Note ID (`note`) - 个人资料引用 (`nprofile`) - 事件引用 (`nevent`) - 可寻址实体 (`naddr`) - 中继 URL (`nrelay`) - 全面的验证和错误处理 - 支持多个中继 URL - 用于复杂实体的 TLV (Type-Length-Value) 编码 - 改进的类型导出和函数可访问性 (v0.4.5+) - 增强的错误消息和验证 - 保证跨平台兼容性 ### NIP-19 使用示例 ``` import { npubEncode, nsecEncode, noteEncode, nprofileEncode, neventEncode, naddrEncode, nrelayEncode, decode } from 'nostr-crypto-utils'; // Encode a public key const npub = npubEncode('7f3b6c2444c526fc7b3a48b0a1e38fb6a5a4062d4a097c9e96feb3c1df2f36d0'); // npub1xtscya34g58tk0z6v2g0r6w5gpqrxdgkz9xeav // Encode a private key const nsec = nsecEncode('7f3b6c2444c526fc7b3a48b0a1e38fb6a5a4062d4a097c9e96feb3c1df2f36d0'); // nsec1xtscya34g58tk0z6v2g0r6w5gpqrxdgkz9xeav // Encode a note ID const note = noteEncode('7f3b6c2444c526fc7b3a48b0a1e38fb6a5a4062d4a097c9e96feb3c1df2f36d0'); // note1xtscya34g58tk0z6v2g0r6w5gpqrxdgkz9xeav // Encode a profile with multiple relays const nprofile = nprofileEncode( '7f3b6c2444c526fc7b3a48b0a1e38fb6a5a4062d4a097c9e96feb3c1df2f36d0', ['wss://relay1.example.com', 'wss://relay2.example.com'] ); // Encode an event with author and kind const nevent = neventEncode( '7f3b6c2444c526fc7b3a48b0a1e38fb6a5a4062d4a097c9e96feb3c1df2f36d0', ['wss://relay.example.com'], '7f3b6c2444c526fc7b3a48b0a1e38fb6a5a4062d4a097c9e96feb3c1df2f36d0', 1 ); // Encode an addressable entity (NIP-33) const naddr = naddrEncode( '7f3b6c2444c526fc7b3a48b0a1e38fb6a5a4062d4a097c9e96feb3c1df2f36d0', 30023, 'my-article', ['wss://relay.example.com'] ); // Encode a relay URL const nrelay = nrelayEncode('wss://relay.example.com'); // Decode any bech32-encoded entity const decoded = decode(nprofile); console.log(decoded); // { // type: 'nprofile', // data: '7f3b6c2444c526fc7b3a48b0a1e38fb6a5a4062d4a097c9e96feb3c1df2f36d0', // relays: ['wss://relay1.example.com', 'wss://relay2.example.com'] // } // All functions include validation try { const invalidNpub = npubEncode('invalid-hex'); } catch (error) { console.error(error); // Error: Invalid hex string } ``` ### NIP-44 使用(加密 Payload) ``` import { nip44 } from 'nostr-crypto-utils'; // Or via subpath: import * as nip44 from 'nostr-crypto-utils/nip44'; // Derive a conversation key from ECDH const conversationKey = nip44.getConversationKey(myPrivKeyBytes, theirPubkeyHex); // Encrypt const encrypted = nip44.encrypt('Hello!', conversationKey); // Decrypt const plaintext = nip44.decrypt(encrypted, conversationKey); ``` ### NIP-46 使用(远程签名) ``` import { nip46 } from 'nostr-crypto-utils'; // Or via subpath: import * as nip46 from 'nostr-crypto-utils/nip46'; // Parse a bunker:// URI const bunker = nip46.parseBunkerURI('bunker://pubkey...?relay=wss://relay.example.com&secret=abc'); // Create a session (ephemeral keypair + NIP-44 conversation key) const session = nip46.createSession(bunker.remotePubkey); // Build a request const req = nip46.connectRequest(bunker.remotePubkey, bunker.secret); // Wrap into a kind 24133 encrypted event (ready to publish to relay) const event = await nip46.wrapEvent(req, session, bunker.remotePubkey); // On receiving a response event, unwrap it const response = nip46.unwrapEvent(responseEvent, session); // Create a filter for subscribing to responses const filter = nip46.createResponseFilter(session.clientPubkey); ``` ### NIP-49 使用(ncryptsec 密钥加密) ``` import { nip49 } from 'nostr-crypto-utils'; // Or via subpath: import * as nip49 from 'nostr-crypto-utils/nip49'; // Encrypt a private key with a password const ncryptsec = nip49.encrypt(secretKeyBytes, 'my-password'); // Returns: 'ncryptsec1...' // Decrypt it back const secretKey = nip49.decrypt(ncryptsec, 'my-password'); ``` ### NIP-98 使用(HTTP Auth) 构建并验证 `kind 27235` 认证事件以及 `Authorization: Nostr ` 头部。**不会发起任何 HTTP 请求** —— 您需要自行发起请求,这使得该 包保持 Edge 原生。非常适合用于向 Nostr 原生的 HTTP 服务进行请求认证。 ``` import { nip98 } from 'nostr-crypto-utils'; // Or via subpath: import * as nip98 from 'nostr-crypto-utils/nip98'; // --- client side --- const event = await nip98.createAuthEvent( { url: 'https://api.example.com/v1/me', method: 'GET' }, privateKeyHex, ); const res = await fetch('https://api.example.com/v1/me', { headers: { Authorization: nip98.toAuthHeader(event) }, }); // POST with a body — the body is hashed into a `payload` tag const body = JSON.stringify({ name: 'alice' }); const postAuth = await nip98.createAuthEvent( { url: 'https://api.example.com/v1/register', method: 'POST', payload: body }, privateKeyHex, ); // --- server side (e.g. a Cloudflare Worker) --- const incoming = nip98.fromAuthHeader(request.headers.get('Authorization')); const result = await nip98.validateAuthEvent(incoming, { url: request.url, method: request.method, body: await request.text(), // optional, checks the payload tag when present }); if (!result.valid) return new Response(result.reason, { status: 401 }); const authedPubkey = incoming.pubkey; ``` ### NIP-59 使用(Gift Wrap) 封装任何事件:一个 rumor(未签名)会被密封(`kind 13`)并使用 NIP-44 加密进行 gift wrap(`kind 1059`,或临时的 `kind 21059`)。解包时会验证 seal 签名并强制要求 seal 作者等于 rumor 作者。 ``` import { nip59 } from 'nostr-crypto-utils'; // Or via subpath: import * as nip59 from 'nostr-crypto-utils/nip59'; // Build a rumor and gift-wrap it to a recipient const rumor = await nip59.createRumor({ kind: 1, content: 'hello' }, senderPubkeyHex); const giftWrap = await nip59.wrapEvent(rumor, senderPrivkeyHex, recipientPubkeyHex); // giftWrap.kind === 1059, signed by a random one-time key, p-tagged to the recipient // Recipient unwraps it back to the rumor const recovered = await nip59.unwrapEvent(giftWrap, recipientPrivkeyHex); // recovered.content === 'hello', recovered.pubkey === sender ``` ### NIP-17 使用(私有私信) 基于 NIP-44 + NIP-59 构建的加密聊天。`createDirectMessage` 为每个接收者返回一个 gift wrap, **外加**一份发送给自己的副本;将它们分别发布到对应 接收方的私信收件箱中继(NIP-17 `kind 10050`)。 ``` import { nip17 } from 'nostr-crypto-utils'; // Or via subpath: import * as nip17 from 'nostr-crypto-utils/nip17'; // Send a DM (also works for group rooms via multiple recipients) const wraps = await nip17.createDirectMessage(senderPrivkeyHex, { content: 'hey bob', recipients: [bobPubkeyHex], subject: 'dinner', // optional }); for (const { recipient, giftWrap } of wraps) { // publish giftWrap to `recipient`'s DM relays } // Read a received gift wrap into the kind-14 chat message const message = await nip17.readDirectMessage(receivedGiftWrap, recipientPrivkeyHex); // message.kind === 14, message.content === 'hey bob' ``` ### 类型系统 #### 事件类型 该库为 Nostr 事件提供了一个全面的类型系统: ``` // Create an unsigned event const unsignedEvent: UnsignedNostrEvent = { kind: NostrEventKind.TEXT_NOTE, content: "Hello Nostr!", tags: [], created_at: Math.floor(Date.now() / 1000) }; // Sign the event const signedEvent = await signEvent(unsignedEvent, privateKey); ``` #### 认证 (NIP-42) 支持 NIP-42 认证: ``` // Create an auth event const authEvent: UnsignedNostrEvent = { kind: NostrEventKind.AUTH, content: "challenge-response", tags: [["challenge", challenge]], created_at: Math.floor(Date.now() / 1000) }; // Sign and send the auth event const signedAuthEvent = await signEvent(authEvent, privateKey); ``` #### 自定义标签 (NIP-12) 支持任意标签: ``` // Create a filter with custom tags const filter: NostrFilter = { kinds: [NostrEventKind.TEXT_NOTE], "#t": ["nostr", "crypto"], // Filter by custom tag limit: 10 }; ``` ### 快速开始 ### 安装 ``` npm install nostr-crypto-utils ``` ### 基本用法 #### NIP-19 编码(人类可读格式) ``` import { npubEncode, nsecEncode, noteEncode } from 'nostr-crypto-utils'; // Convert a hex public key to npub format const hexPubkey = '12xyl6w6aacmqa3gmmzwrr9m3u0ldx3dwqhczuascswvew9am9q4sfg99cx'; const npub = npubEncode(hexPubkey); console.log(npub); // npub1... // Convert a hex private key to nsec format const hexPrivkey = 'your-hex-private-key'; const nsec = nsecEncode(hexPrivkey); console.log(nsec); // nsec1... // Convert an event ID to note format const eventId = 'your-event-id'; const note = noteEncode(eventId); console.log(note); // note1... ``` #### 密钥生成和事件签名 ``` import { generateKeyPair, createEvent, signEvent } from 'nostr-crypto-utils'; // Generate a new key pair const keyPair = generateKeyPair(); console.log(keyPair.publicKey, keyPair.privateKey); // Create and sign an event const event = createEvent({ pubkey: keyPair.publicKey, kind: 1, content: 'Hello Nostr!' }); const signedEvent = signEvent(event, keyPair.privateKey); ``` ### Schnorr 签名示例 该库为 Schnorr 签名提供了强大的支持,这是 Nostr 加密操作的基础。以下是一些常见的用例: #### 基本消息签名 ``` import { schnorrSign, schnorrVerify } from 'nostr-crypto-utils'; // Sign a message const message = 'Hello Nostr!'; const privateKey = 'your-private-key'; const signature = await schnorrSign(message, privateKey); // Verify the signature const publicKey = 'corresponding-public-key'; const isValid = await schnorrVerify(signature, message, publicKey); console.log('Signature valid:', isValid); // true ``` #### 委托 token 示例 (NIP-26) ``` import { schnorrSign } from 'nostr-crypto-utils'; // Create a delegation token const delegateePubkey = 'delegatee-public-key'; const delegatorPrivkey = 'delegator-private-key'; const conditions = { kind: 1, // Only allow text notes until: Math.floor(Date.now() / 1000) + 86400 // 24 hours from now }; // Format conditions string const conditionsString = Object.entries(conditions) .map(([key, value]) => `${key}=${value}`) .sort() .join('&'); // Create and sign the delegation message const message = `nostr:delegation:${delegateePubkey}:${conditionsString}`; const token = await schnorrSign(message, delegatorPrivkey); ``` 这些示例展示了该库类型安全的 Schnorr 签名方法,确保了安全性和易用性。 ### 加密私信 (NIP-04) 该库提供了遵循 NIP-04 规范的强大加密私信支持: ``` import { encryptMessage, decryptMessage, asPrivateKey, asPublicKey } from 'nostr-crypto-utils'; // Encrypting a direct message: (message, senderPrivkey, recipientPubkey) function createEncryptedDM( content: string, recipientPubkey: string, senderPrivkey: string ) { const encryptedContent = encryptMessage( content, asPrivateKey(senderPrivkey), asPublicKey(recipientPubkey), ); return { kind: 4, // NIP-04 Encrypted Direct Message content: encryptedContent, tags: [['p', recipientPubkey]], }; } // Decrypting a received message: (ciphertext, recipientPrivkey, senderPubkey) function decryptDM( encryptedContent: string, senderPubkey: string, recipientPrivkey: string ) { return decryptMessage( encryptedContent, asPrivateKey(recipientPrivkey), asPublicKey(senderPubkey), ); } ``` ### 事件验证和签名验证 该库提供了用于验证 Nostr 事件和验证签名的实用工具: ``` import { validateEvent, verifySignature } from 'nostr-crypto-utils'; import type { NostrEvent, SignedNostrEvent } from './types'; async function validateSignedMessage(event: NostrEvent): Promise { try { // First validate the event structure if (!validateEvent(event)) { console.debug('Invalid event format'); return false; } // Then verify the signature const isValid = await verifySignature(event as SignedNostrEvent); if (!isValid) { console.debug('Invalid signature'); return false; } return true; } catch (error) { console.error('Error validating signed message:', error); return false; } } ``` 这些示例展示了来自生产环境 Nostr 应用的实际使用模式,展示了该库的安全特性和易用性。 ### NIP-26 功能 - 创建委托 token - 使用委托对事件进行签名 - 验证委托事件 - 带有以下条件的委托: - 时间限制 - 事件类型限制 - 标签限制 ## 功能与特性 | 特性 | 状态 | 描述 | |---------------------------|--------|-------------------------------------------------------| | 密钥管理 | ✅ | 生成、验证和管理 Nostr 密钥对 | | 事件创建 | ✅ | 创建并验证 Nostr 事件 | | 事件签名 | ✅ | 使用 schnorr 签名对事件进行签名 | | 事件验证 | ✅ | 验证事件签名并验证事件结构 | | 私 (NIP-04) | ✅ | 加密和解密私信 | | 消息格式化 | ✅ | 为中继通信格式化消息 | | 类型安全 | ✅ | 提供严格类型的完整 TypeScript 支持 | | 浏览器支持 | ✅ | 在带有 Web Crypto API 的现代浏览器中运行 | | Node.js 支持 | ✅ | 完全支持 Node.js 环境 | ## 与 nostr-nsec-seedphrase 集成 该库旨在与 [nostr-nsec-seedphrase](https://github.com/HumanjavaEnterprises/nostr-nsec-seedphrase) 无缝协作,为 Nostr 密钥管理和加密操作提供完整的解决方案: ``` import { generateSeedPhrase } from 'nostr-nsec-seedphrase'; import { createTextNoteEvent, signEvent } from 'nostr-crypto-utils'; // Generate keys using nostr-nsec-seedphrase const seedPhrase = generateSeedPhrase(); const keyPair = seedPhraseToKeyPair(seedPhrase); // Use nostr-crypto-utils for event creation and signing const event = createTextNoteEvent({ content: 'Hello Nostr!', pubkey: keyPair.publicKey, created_at: Math.floor(Date.now() / 1000) }); const signedEvent = signEvent(event, keyPair.privateKey); ``` 这些库共同提供: - 通过 seed phrase 进行安全的密钥生成和恢复 - 类型安全的加密操作 - 全面的事件处理 - 最小的打包体积和依赖 ## 委托 token 创建 (NIP-26) 您可以使用此库创建用于 Web 服务器或其他应用程序的委托 token。这实现了 [NIP-26](https://github.com/nostr-protocol/nips/blob/master/26.md) 以进行签名权限的委托。 ### 基本委托示例 ``` import { createDelegation, validateDelegation } from 'nostr-crypto-utils'; // Create a delegation token (delegator's perspective) const delegatorKeyPair = await generateKeyPair(); const delegateePubkey = 'npub1...'; // The public key you're delegating to const delegation = await createDelegation({ delegatorPrivkey: delegatorKeyPair.privateKey, delegateePubkey, conditions: { kind: 1, // Only allow text notes until: Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60 // 30 days } }); // Validate a delegation token (delegatee's perspective) const isValid = await validateDelegation({ token: delegation.token, delegatorPubkey: delegatorKeyPair.publicKey, delegateePubkey, kind: 1 }); ``` ### Web 服务器示例 以下是如何在 Web 服务器上下文中使用委托 token 的示例: ``` import { createEvent, signEventWithDelegation } from 'nostr-crypto-utils'; // On your server, store these securely const DELEGATE_PRIVKEY = 'nsec1...'; // Your server's private key const DELEGATION_TOKEN = 'nostr:delegation:...'; // Token from the delegator const DELEGATOR_PUBKEY = 'npub1...'; // Delegator's public key // Create and sign an event on behalf of the delegator const event = await createEvent({ kind: 1, content: 'Posted via delegation!', pubkey: DELEGATOR_PUBKEY, // Original delegator's pubkey created_at: Math.floor(Date.now() / 1000) }); const signedEvent = await signEventWithDelegation({ event, delegatePrivkey: DELEGATE_PRIVKEY, delegation: { token: DELEGATION_TOKEN, conditions: { kind: 1, until: Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60 } } }); ``` ### 条件委托 您可以创建带有条件的更具体的委托: ``` // Create a delegation with multiple conditions const delegation = await createDelegation({ delegatorPrivkey: delegatorKeyPair.privateKey, delegateePubkey, conditions: { kinds: [1, 6], // Allow only text notes and reposts until: Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60, // 1 week since: Math.floor(Date.now() / 1000), // Starting from now tags: [ ['t', 'nostr'], // Only allow posts with tag 't' = 'nostr' ['p', delegateePubkey] // Only allow mentions of the delegatee ] } }); ``` ### 安全注意事项 处理委托 token 时: 1. **安全存储**:始终安全地存储委托 token 和私钥 2. **检查过期时间**:在使用前验证委托的时间限制 3. **验证条件**:在签名或接受委托事件之前检查所有条件 4. **限制范围**:仅委托最低要求的权限 5. **监控使用情况**:跟踪委托 token 的使用方式 有关委托的更多详细信息,请参阅 [NIP-26 规范](https://github.com/nostr-protocol/nips/blob/master/26.md)。 ## 类型系统改进 ### 增强的类型系统 - **统一的类型定义**:通过统一的类型定义提高一致性和安全性 - **更好的类型推断**:增强的类型推断使开发更容易,代码补全更完善 - **更严格的类型检查**:通过更严格的检查改进类型安全,以更好地预防错误 ### 改进的类型文档 - **更好的 JSDoc 注释**:通过清晰简洁的 JSDoc 注释改进文档 - **NIP 参考**:添加了对相关 NIP 的引用,以便更好地理解底层协议 ## 调试模式 启用调试模式以获取详细的日志记录: ``` import { setDebugLevel } from 'nostr-crypto-utils'; // Enable debug logging setDebugLevel('debug'); // Or for even more detail setDebugLevel('trace'); ``` ### 常见错误消息 | 错误消息 | 可能的原因 | 解决方案 | |--------------|--------------|----------| | "Invalid delegation token" | Token 已过期或格式错误 | 检查 token 的过期时间和格式 | | "Signature verification failed" | 密钥格式错误或签名损坏 | 验证密钥格式和转换 | | "Condition check failed" | 事件不符合委托条件 | 检查事件类型和其他条件 | | "Invalid pubkey format" | 使用了 bech32 而不是 hex | 将 pubkey 转换为正确的格式 | | "Token expired" | 委托 token 已过期 | 创建具有未来过期时间的新委托 | ### 验证检查 进行故障排除时,请验证以下常见问题: #### 密钥格式 - 私钥应为 hex 格式 - 公钥应为 hex 格式(而不是 bech32) - 签名应为 64 字节的 hex 格式 #### 时间限制 - Token 过期时间应该在未来 - 检查系统时钟同步 - 始终使用 UTC 时间戳 #### 权限范围 - 验证事件类型是否符合委托条件 - 检查是否有任何标签限制 - 确认时间窗口限制 #### 网络问题 - 验证中继连接 - 检查速率限制 - 确认正确的 websocket 处理 ### 测试工具 ``` // Test delegation validity const testDelegation = async (delegation) => { const result = await validateDelegation({ token: delegation.token, delegatorPubkey: delegation.delegator, delegateePubkey: delegation.delegatee, kind: 1 }); console.log('Delegation valid:', result.isValid); if (!result.isValid) { console.error('Validation error:', result.error); } return result; }; // Test event signing const testEventSigning = async (event, delegation) => { try { const signed = await signEventWithDelegation({ event, delegatePrivkey: delegation.privateKey, delegation: delegation.token }); console.log('Event signed successfully:', signed.id); return true; } catch (error) { console.error('Signing failed:', error); return false; } }; ``` 如需更多帮助,请加入我们的 [Discord 社区](https://discord.gg/nostr)或[提交 issue](https://github.com/humanjavaenterprises/nostr-crypto-utils/issues)。 ## 贡献 我们欢迎各种形式的贡献!详情请参阅我们的[贡献指南](CONTRIBUTING.md)。 ## 许可证 本项目基于 MIT 许可证授权 —— 有关详细信息,请参阅 [LICENSE](LICENSE) 文件。 ## 更新日志 有关更改的详细历史记录,请参阅 [CHANGELOG.md](CHANGELOG.md)。 ## 支持 - [文档](https://humanjavaenterprises.github.io/nostr-crypto-utils/) - [Issue 追踪器](https://github.com/HumanjavaEnterprises/nostr-crypto-utils/issues) - [讨论区](https://github.com/HumanjavaEnterprises/nostr-crypto-utils/discussions) ## 相关项目 - [nostr-nsec-seedphrase](https://github.com/HumanjavaEnterprises/nostr-nsec-seedphrase) - 使用 BIP-39 seed phrase 生成和管理 Nostr 私钥
标签:CVE, MITM代理, Nostr协议, TypeScript, 加密解密, 安全插件, 密码学, 开发工具库, 手动系统调用, 数字签名, 程序员工具, 自动化攻击