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`)
[](https://www.npmjs.com/package/nostr-crypto-utils)
[](http://www.typescriptlang.org/)
[](https://github.com/HumanjavaEnterprises/nostr-crypto-utils/blob/main/LICENSE)
[](https://humanjavaenterprises.github.io/nostr-crypto-utils/)
[](https://github.com/HumanjavaEnterprises/nostr-crypto-utils/actions)
[](https://coveralls.io/github/HumanjavaEnterprises/nostr-crypto-utils?branch=main)
[](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, 加密解密, 安全插件, 密码学, 开发工具库, 手动系统调用, 数字签名, 程序员工具, 自动化攻击