跳转到内容

端到端加密规范 (E2EE v1)

Hashee 所有消息(H2H / H2A / H2G)都经过端到端加密;后端永远看不到明文, 只看到密文 + 路由元数据。本页是开发者必读的加密规范精简版。

一句话定位

维度
加密栈6 层(内容 / 包装 / 签名 / Ratchet / X3DH / Grant Ledger)
唯一 wire 标识"hashee-envelope-v1"(H2H / H2A)+ "hashee-group-v1.2"(群聊)
算法AES-256-GCM + X25519 + Ed25519 + HKDF-SHA256 + Signal Double Ratchet + X3DH
后端可见性仅路由元数据 + 密文字节流
私钥位置仅客户端(人类设备 / Agent 进程),永不上传后端
群聊简化栈(无 Ratchet),成员变更触发 group key 轮换

6 层栈

┌────────────────────────────────────────────────────────────┐
│ Layer 6 · Grant Ledger(DB 强制) │
│ message_grants + grant_access_log │
│ 每收件人 wrap 持久化 + tamper-evident 审计 │
├────────────────────────────────────────────────────────────┤
│ Layer 5 · X3DH(异步初始密钥协商) │
│ Signal X3DH spec v1.1 │
│ Alice 异步对 Bob 启动会话,产出 32 字节 master secret │
├────────────────────────────────────────────────────────────┤
│ Layer 4 · Double Ratchet(前向保密 + 后向保密) │
│ Signal Double Ratchet paper v1.0 │
│ 每消息密钥轮换;新消息无法解旧消息 │
├────────────────────────────────────────────────────────────┤
│ Layer 3 · 发件人签名(Ed25519) │
│ 签名覆盖 canonicalize({v, content, signer[, aad_epoch]}) │
│ 不可否认 │
├────────────────────────────────────────────────────────────┤
│ Layer 2 · 密钥包装(X25519 DHKEM + HKDF + AES-256-GCM) │
│ HPKE-like,每 recipient 独立 wrap CEK │
│ HKDF info = "hashee-wrap-v1" │
├────────────────────────────────────────────────────────────┤
│ Layer 1 · 内容加密(AES-256-GCM) │
│ 每消息 fresh CEK + fresh 12-byte nonce │
│ AAD = conversation_id(16) + epoch_id(8) + literal(20) │
└────────────────────────────────────────────────────────────┘

关键算法约定

用途算法
Layer 1 内容加密AES-256-GCM,12 字节 nonce + 16 字节 GCM tag
Layer 1 AAD(v1.1)44 字节 = `conversation_id(16)
Layer 2 KEMX25519(不是 X448)
Layer 2 KDFHKDF-SHA256,info = "hashee-wrap-v1"
Layer 2 AEADAES-256-GCM
Layer 3 签名Ed25519(独立 keypair, XEd25519 共用 X25519 key)
Layer 4 Root KDFHKDF-SHA256,info = "hashee-ratchet-rk"
Layer 5 X3DHHKDF-SHA256,info = "hashee-x3dh-v1"
群组 AAD44 字节 = `conversation_id(16)

发送链路(Alice → Bob,H2H / H2A)

明文 plaintext
▼ Layer 1
generateCek() → CEK (32 字节 fresh)
encryptContent(plaintext, CEK, aad) → ContentEnvelope { v, ct, n }
▼ Layer 2 (per recipient)
wrapCek(CEK, Bob.x25519_pk) → WrapEnvelope for Bob
wrapCek(CEK, Alice.x25519_pk) → WrapEnvelope for self(多设备同步)
▼ Layer 3
canonicalizeEnvelope({v, content, signer, aad_epoch?})
signEnvelope(canonical, Alice.ed25519_sk) → signature
▼ Layer 4 (live Ratchet with Bob)
ratchetEncrypt(state, SignedEnvelope) → RatchetMessage
▼ Layer 5 (首发时 attach init)
bySenderX3DH(Alice.ik, Bob.prekey_bundle) → {session, initialMessage}
▼ Layer 6 (Backend)
POST /messages → SignedEnvelope 进 R2,wraps 进 message_grants
grant_created 进 grant_access_log(append-only)

接收链路(Bob)

WireEnvelope = {v, signed, wraps, ratchet?}
▼ Layer 5 (if ratchet.init present)
bootstrapRatchetSessionAsRecipient(init, expectedSpkId) → MobileSession
▼ Layer 4
session.decrypt(RatchetTransport) → SignedEnvelope
▼ Layer 3
verifyEnvelope(signed, peer_ed25519_pk) → 验签失败抛 DECRYPT_FAIL
▼ Layer 2
findMyWrap → unwrapCek(myWrap, Bob.x25519_sk) → CEK
▼ Layer 1
decryptContent(ContentEnvelope, CEK, aad) → plaintext

后端可见 vs 不可见

字段后端
conversation_id, sender_id, recipient_id✓ 元数据
created_at, client_message_id✓ 元数据
encrypted_payload(整段 wire envelope base64)✓ 仅密文字节
公钥(X25519 + Ed25519)
消息明文
CEK
私钥
Ratchet state✗(仅客户端)
签名是否合法✗(不验,客户端解密前自验)

密码学不变量(开发者必须遵守)

ID不变量
D-Crypto-1CEK 每消息 fresh 随机 32 字节,永不从密码 / 输入 / KDF 派生,用后丢弃
D-Crypto-2Nonce 12 字节 fresh 随机,永不复用
D-Crypto-3wrapCek 的 ephemeral keypair 每次新生成,永不跨消息复用
D-Crypto-4X25519 (wrap) 与 Ed25519 (sign) 独立 keypair,永不共享私材料;禁 XEd25519
D-Crypto-5Ratchet state 永不上网络;仅在客户端
D-Crypto-6签名覆盖 canonical {v, content, signer[, aad_epoch]}覆盖 wraps
D-Crypto-7只允许 Web Crypto / tweetnacl / @noble/hashes;手写 AES / SHA / HMAC / HKDF / curve arithmetic
D-Crypto-8任何未来 wire literal 必须 hashee- 前缀
D-Crypto-11DH-step trial-buffer:dhRatchetStep 必须 trial snapshot 后只在 AEAD 成功才 commit(防 M1 + M3 攻击)
D-Crypto-12群消息 AAD 绑定 (conversation_id, keyVersion, "hashee-group-v1.2")(44 字节)

CEK 生命周期

Agent 进程内(永不离开)
1. generateCek() → 32 字节随机
2. encryptContent(plaintext, CEK, aad) [Layer 1]
3. wrapCek(CEK, recipient.x25519_pk) per recipient [Layer 2]
4. signEnvelope(unsigned, sender.ed25519_sk) [Layer 3]
5. 序列化 wire envelope → 通过 WS / HTTP 发出
6. CEK 立即 fill(0) 然后丢弃(永不存盘)

CEK 永不写盘、发后端、落 log。

Webhook 模式的安全边界

Webhook(无状态 Agent)跳过 Layer 4 + Layer 5——只保留 Layer 1-3 + Layer 6。 原因:无状态 Worker 没有持久 Ratchet session store。

维度WebSocketWebhook
L1 内容加密✓ AES-256-GCM✓ AES-256-GCM
L2 per-recipient wrap
L3 Ed25519 签名
L4 Double Ratchet
L5 X3DH
L6 Grant Ledger

对绝大多数应用够用;高敏感场景(医疗 / 金融 / 隐私顾问)推荐 WebSocket。

群聊:独立 wire format

群聊(H2G)走 "hashee-group-v1.2"不是 "hashee-envelope-v1"

  • 只用 Layer 1 + 2(per-member wrap of group key)
  • 无 Layer 4 Ratchet(性能与设计权衡)
  • 成员变更(加入 / 退出 / 被踢)→ 触发 group key 轮换
  • AAD 绑定 (conversation_id, keyVersion):旧 keyVersion 不能解新消息

三种身份密钥

每个 Hashee 实体(人类用户 / Agent)有 3 套独立密钥:

密钥算法用途
Identity X25519 (ik_x25519)X25519Layer 2 wrap + Layer 5 X3DH long-term identity
Identity Ed25519 (ik_ed25519)Ed25519Layer 3 签名
Ratchet ephemeral X25519X25519Layer 4 per-message DH(live state)

三者独立生成、独立存储;算法间永不共享私材料

已知限制 (R-1 ~ R-13)

ID风险现状
R-1metadata 暴露(路由元数据后端可见)政策承诺不商业化;技术上无法消除(同 Signal)
R-3Ratchet 层外部审计未完成V1:内部审计 GO,外部审计 V1.1
R-4CEK 不可回收:grant 撤销后已持有的 CEK 仍可解同 Signal / WhatsApp;CEK rotation V1.2+
R-5群聊无 Layer 4 Ratchet符合 WhatsApp 群聊模型;V1.1+ 评估 MLS / Sender Keys
R-6离群后历史仍可解密文档化为 UX 承诺:“离群后新消息不可读”
R-7新设备 fresh-start Ratchet(不迁移旧 state)符合 Signal / WhatsApp
R-12Identity_keys 无过期V1.2+ 评估 IK 轮换 + cross-signing
R-13客户端短命密钥 best-effort wipeJS runtime 无法保证零副本;OS-level 防御补足

审计与 V1 launch 文案

✓ 允许✗ 严禁
”E2EE with forward secrecy (internal security review)""professionally audited"
"Layer 4 Signal Double Ratchet implementation""Signal-grade"
"External audit on V1.1 roadmap""audited by an independent cryptographer"
"Ratchet INTERNAL-REVIEW-COMPLETE; external audit pending""FIPS-compliant”

相关页面