v1 协议, 丝毫不差。
下面的一切都是依据冻结并已发布的代码撰写的。每个常量都可在 代码仓库 中核查,跨实现测试向量固定了字节级行为。如果本页与代码有任何不一致,以代码为准,而该不一致是一个值得报告的缺陷。
简短版本
- 纸条正文在发件人的设备上加密。任何服务器都从不见到它。
- 解密密钥只在链接片段中传递,即
#之后的部分,浏览器从不将其发送给任何服务器。 - 解密在收件人的浏览器中进行,使用任何人都能通过「查看源代码」读到的 JavaScript。
- 支撑焚毁、到期和状态的管理令牌能控制纸条,但永远无法解密它。服务器只存储它的 SHA-256 哈希。
- 创建完成界面关闭后,完整链接便无法重建。应用只保留纸条 id 和令牌,从不保留密钥。
链接
burnpony.app/n/{id}#{key}{flags}。id 是服务器从 16 个随机字节铸造的 22 个 base64url 字符。key 是 43 个无填充的 base64url 字符,编码在发件人设备上生成的恰好 32 个随机字节。可选的 flags 后缀是一个句点加字母:p 表示需要口令,r 表示发件人启用了已读回执,因此页面能在消耗查看次数的显示之前把这两件事告诉你。# 之后的一切都是 URL 片段,浏览器不会在请求中发送片段。密钥从不到达任何服务器、不出现在任何日志中,也不是所存纸条的一部分。这一个属性支撑起整个设计。
信封
服务器存储的是一个不透明的 JSON 字符串:{"v":1,"pw":false,"salt":"…","nonce":"…","ct":"…"} — 一个格式版本、口令标志、16 字节盐、12 字节随机数,以及附加了 16 字节认证标签的 AES-256-GCM 密文,全部为标准 base64。信封中键的顺序无关紧要;它按 JSON 解析。
载荷,逐字节
加密之下的明文是 {"v":1,"t":"<text>","ah":<seconds>} — 即纸条(最多 50,000 个字符)和自动隐藏计时器。它的序列化在每一种实现中都是确定的:键严格按该顺序、无空白、转义仅限 \" \\ \n \r \t \b \f 以及用于其余控制字符的 \u00XX,其他一切都按 UTF-8 原样输出。Swift、WebCrypto 和 Python 参考实现产生相同的字节,共享向量对此加以强制。
密钥派生
加密密钥是 HKDF-SHA256(ikm, salt, info = "BurnPony-v1-key", 32 bytes)。没有口令时,ikm 就是片段密钥。有口令时,口令用 PBKDF2-HMAC-SHA256 在同一盐上迭代 60 万次强化,并先附加到片段密钥前。在每一种实现中,口令在强化前都会规范化:Unicode NFC 规范化,然后去除包括 U+FEFF 在内的首尾空白。多余的键盘空格或分解的 Unicode 输入会被宽恕;而大小写和内部空格的更改则被有意地不宽恕。错误的口令和被篡改的密文都只是 GCM 认证失败;没有任何东西能区分二者。
API
/api 下有六个操作,全部为 JSON。POST /notes 存储一个信封,附带查看上限(1–100)、到期(300 至 2,592,000 秒)和回执标志,返回纸条 id 和一个一次性的 64 位十六进制管理令牌。GET /notes/{id} 在事务内消耗一次查看,并在达到上限时将密文置空。使用管理令牌:GET …/status 报告计数器和回执时间,DELETE 立即焚毁,PATCH 从现在起重新计时一张存活的纸条,POST /notes/{id}/push 为回执投递注册最多五个 APNs 或 FCM 令牌。POST /report/{id} 在不消耗查看次数的情况下提交滥用举报。令牌在服务器端只以 SHA-256 哈希存储,任何可能区分错误令牌、已焚毁纸条和从未存在纸条的失败,都返回逐字节相同的 {"error":"not_found"} — 这种恒定是有意为之。
服务器存储什么
密文(在焚毁或耗尽时置空)、查看计数器、时间戳、令牌哈希、回执时间戳、回执纸条的推送令牌、以本地密钥哈希的限流计数器,以及滥用举报。无账户、无邮箱、无原始 IP 地址、无分析。一个清扫器会删除过期行,但到期也在读取时强制执行,因此失效的 cron 从不延长纸条的寿命。
已读回执,从机制上看
回执在收件人显示之前就已向其披露 — 这正是 r 标志存在的意义。推送载荷刻意笼统:提醒只说一张纸条被打开了,唯一的自定义键是纸条 id。发件人自己的设备使用一个只存在于该设备、别处皆无的标签在本地重写通知;服务器从不知道任何标签。
自己验证
三项检查,无需信任。其一:收件人页面可复现 — 仓库中的 scripts/assemble_viewer.py --verify 证明交付的 viewer.html 与其三个可审计部分逐字节一致,而在任意存活纸条上「查看源代码」可与已发布文件比对。其二:密码学被固定 — shared/burnpony_vectors.json 保存来自独立实现的基准真值,Swift 测试套件和内嵌的 WebCrypto 核心都能精确复现每一段密文。其三:服务器的贫乏可读 — server/schema.sql 和 API 路由短到可以一口气读完,其中没有任何可泄露之物。诚实的限制无论如何都成立:已显示的纸条总能被复制或拍照,运营者也可能提供与已发布不同的代码 — 而这恰恰是页面可复现、这份规范存在的原因,以便替换可被察觉。