news 2026/9/10 20:23:23

2of3 入门实战:用 Shamir 2-of-3 门限把 Ente 恢复密钥拆成三张卡

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
2of3 入门实战:用 Shamir 2-of-3 门限把 Ente 恢复密钥拆成三张卡

2of3 入门实战:用 Shamir 2-of-3 门限把 Ente 恢复密钥拆成三张卡

【免费下载链接】ente💚 End-to-end encrypted cloud for everything.项目地址: https://gitcode.com/GitHub_Trending/en/ente

导读:本文以 Ente 开源仓库中 2of3 入门文档 为主体,完整讲解 2of3 的完整生命周期——把一份秘密拆成三张卡、分点存储、在收起来之前测试恢复,以及日后随时用任意两张卡恢复秘密。2of3 是 Ente 提供的免费端到端私密网页工具(2of3.ente.com),无需账号、无需安装,秘密始终只停留在你的浏览器里。读完本文,你将掌握 2of3 的全部操作步骤、卡片的存储策略、错误信息的含义,以及其背后 Shamir 秘密共享的数学原理与开源实现。

什么是 2of3

2of3 把一份重要秘密变成三张恢复卡:任意两张卡都能还原出秘密,单张卡无法还原。它是一个网页应用,不需要账号、不需要安装,你的秘密永远不会离开浏览器。适用于 Ente 恢复密钥、密码管理器主密码、钱包助记词、全盘加密密钥、应急(break-glass)生产凭据等"丢不得也泄不得"的秘密。

这一设计思路与需要两把钥匙的保险箱、能容忍任意一个站点宕机的备份磁带轮换是同源的:三份拷贝放在三个地方,丢失任一份不再是灾难;"任意两张"的门限意味着攻击者即使拿到一张卡,也拿不到任何可用的东西。相关背景与使用场景详见 docs/docs/2of3/index.md。

注意:如果你的秘密就是 Ente 账户恢复密钥本身,可以优先考虑 Legacy Kit。Legacy Kit 与 2of3 使用相同的 2-of-3 Shamir 数学,但额外提供了可配置的等待期、卡片丢失时的吊销能力,以及绑定 Ente 账号的服务器中介恢复流程。2of3 则适用于非 Ente 秘密,或希望恢复过程完全不依赖任何服务器、只依赖卡片本身的情况。

将一个秘密拆成三张卡

  1. 打开 2of3.ente.com。
  2. 在左侧的Secret字段粘贴或输入你想要拆分的秘密。可以是恢复密钥、主密码、钱包助记词,或任何一小段文本。字段下方的计数器会实时显示当前已用字节数与上限。
  3. 可选地编辑Card label(卡片标签)。默认显示今天的日期(例如26 May 2026)。标签会打印在每张卡上,方便日后区分不同的卡片组。
  4. 秘密合法后,右侧会立即出现三张卡。每张卡包含一个二维码、卡片编号(1、2、3)以及一个类似A1B2C3D4ID。同一份秘密生成的三张卡共享同一个 ID。

从源码看,"秘密合法后立即生成三张卡"是由splitSecret在浏览器内同步完成的,它调用crypto.getRandomValues生成随机系数、随机 ID,并输出三份编码后的 share,见 web/apps/twoof3/src/features/twoof3/utils/shamir.ts。

[!IMPORTANT]

把三张卡当作一个整体套装对待,绝不要把不同套装的卡片混用。恢复只有在合并的卡片拥有相同 ID、且由同一份秘密生成时才有效。如果秘密改变了,请生成一整套新卡并一起替换掉旧三张。

保存卡片

每张卡都有独立的操作控件,从卡片面板可以:

  • Print(打印):为单张卡打开打印对话框。
  • Download(下载):把该卡保存为 PNG 图片(高分辨率,适合日后打印)。
  • Share(分享):在支持的平台上打开系统分享面板(移动端及部分桌面浏览器)。若不支持分享,则改为下载该卡。
  • Copy code(复制代码):仅复制该卡的文本代码(以2of3-开头),方便粘贴到笔记或密码管理器中。

在源码中,卡片渲染为 1400×1840 的高分辨率 Canvas,二维码区域 860 像素,文本代码按每行最多 80 字符、最多 4 行排版(PRINTED_SHARE_CHARS_PER_LINE = 80PRINTED_SHARE_LINE_COUNT = 4),见 web/apps/twoof3/src/features/twoof3/utils/export.ts 与 renderShareCard。卡面还印有应用名2of3.ente.com、卡片编号、标题与ID指纹,即使多年后被人捡到也能看懂。

对于"想要三张卡外加离线恢复文件"的典型情况,点击秘密字段下方的Download all cards,会下载四个文件:

  • 三张 PNG 图片,每卡一张,命名形如today-set-a1b2c3d4-card-1.png
  • 一个 HTML 文件2of3-recovery.html,它是 2of3 恢复流程的完全离线副本。详见 How it works。

[!NOTE]

2of3 的最大秘密长度约为 200 字节文本。精确上限是"仍然能印得进一张卡"的长度;Secret字段下方的字节计数器会显示当前限制。如果秘密太长放不下,参见 FAQ。

最大长度限制背后的实现

格式本身在原理上支持最大 2048 字节(MAX_SECRET_BYTES = 2048,见 shamir.ts),但实际可用上限取决于"可读二维码 + 四行印刷文本"能否塞进一张卡。源码用maxSecretBytesForEncodedShareLength(MAX_PRINTED_SHARE_LENGTH)计算:卡片文本区最多容纳 80×4 = 320 个字符的编码 share,反推得到可容纳的秘密字节数(约 200 字节),见 export.ts 与 shamir.ts 中的编码长度换算。字节计数器显示的正是这个实时计算的上限。

把三张卡存放在不同的地方

2of3 的全部意义就在于分离。三份拷贝放在同一个抽屉里,那只是"一个抽屉"。

一个合理的分布方案大致是:

  • 一张卡与重要文件放在一起(护照、遗嘱、房产证)。
  • 一张卡放在物理上独立的地方:家里的保险箱、单位的办公桌抽屉,或银行保险箱。
  • 一张卡交给信任的人:配偶、成年子女、遗嘱执行人,或多年挚友。

卡片不一定要全是纸质或全是数字,混合存放完全没问题:

  • 纸质卡容易离线存放,能扛住损坏硬盘的那些意外。
  • 下载的 PNG便于小心复制(备份中的备份,放在保险箱里的 U 盘上),也便于通过端到端加密渠道分享。
  • 复制的文本代码便于粘贴进密码管理器、密封便签或加密保险库。

许多人会希望同一张卡既有一份打印件、又有一份图片,分放在两个地方,这样一次火灾或一次磁盘故障不会带走整张卡。

[!IMPORTANT]

不要把两张卡放在同一个地方。两张卡合在一起就是完整秘密。保护来自物理分离,而不是来自格式本身。

给卡片加上标签,让陌生人也能用

卡片上已经印有应用名(2of3.ente.com)、卡片编号和 ID。对于听说过 2of3 的人来说足够了,但多年后被不熟悉的人捡到的一张卡可能需要提示。考虑贴一张便签或信封,写明:

  • 这份秘密是用来做什么的(让捡到者知道是否要处理它)。
  • 另外两张卡可能在哪里(给捡到者一个着手点)。
  • 提醒:三张中的任意两张就足够了。

如果是作为遗产规划的一部分留下卡片,请写下每套卡片保护的是哪个秘密、应由谁持有哪张卡。

在收起卡片之前测试恢复

这是最重要、也最容易被跳过的一步。

在存储卡片之前,滚动到同一页面的Recover部分,用刚制作的三张卡中的任意两张尝试恢复秘密。这一步能发现:

  • 打印质量差(二维码太淡或污损)。
  • 下载文件被截断。
  • 不小心混入的另一套卡片。
  • 复制粘贴时在代码末尾丢了一个字符。

如果三组可能的配对(1+2、1+3、2+3)都能恢复出秘密,这套卡就是完好的,可以收起来了。这也是 FAQ 中最被强调的一条建议,见 docs/docs/2of3/faq.md。

恢复秘密

日后任意时间,你可以通过以下任一方式恢复:

  • 在 2of3.ente.com 的Recover部分,或
  • 随卡片一起下载的离线恢复 HTML 文件。

两条路径的工作方式完全相同,且都完全在浏览器中运行。

  1. 打开 2of3.ente.com,或从存储位置打开2of3-recovery.html(大多数操作系统上双击即可,会用默认浏览器打开)。
  2. 恢复部分有两个槽位:Card ACard B,二者可互换。
  3. 对每个槽位,执行以下任一操作:
    • Upload image(上传图片):选择保存的卡片 PNG/JPG,或纸质卡的照片。
    • Drag and drop(拖放):把图片文件直接拖到槽位上。
    • Paste code(粘贴代码):把以2of3-开头的文本代码粘贴到Code ACode B字段。
  4. 槽位一旦收到有效卡片,就会显示它是哪张卡,例如Card 2 from ID A1B2C3D4。请确认两个槽位显示的是相同的 ID
  5. 点击Recover secret。原始秘密会出现在Recovered secret框中。点击Copy复制到剪贴板。

恢复校验的每一步(ID 一致性、卡片编号不同、校验和比对)都可以在combineShares中逐行对应:先parseShare解析,再依次检查版本、ID 与长度、卡片编号,最后用 GF(256) 除法重建秘密字节并比对校验和,见 shamir.ts。

从手机照片恢复

纸质卡的照片可以直接使用,因为 2of3 在读取图片时会尝试多种裁剪。如果手机照片把整张卡拍进了画面、二维码对焦清晰,通常第一次就能解码。如果不行:

  • 确保卡片受光均匀,二维码上没有强烈的反光。
  • 尝试更紧的裁剪,尽量只包含二维码。
  • 如果用了低质量扫描,换一张更高分辨率的照片。

背后的机制:恢复流程会对每张图片依次尝试"整图解码、整图加自动方形裁剪、三种逐级收紧的卡片形裁剪"(裁剪区域比例分别为[0.086, 0.213, 0.829, 0.591][0.135, 0.238, 0.73, 0.49][0.16, 0.255, 0.68, 0.52]),第一个成功的尝试胜出。这套"宽容"逻辑同样内嵌在离线恢复文件中。详细原理见 How it works 的 QR decoder 一节,实现见 export.ts 中的 decodeQrImage(对应源码为 offline-qr-source.ts 生成的内联解码器)。

从文本代码恢复

如果你只有文本代码(例如粘贴进了密码管理器),把每个代码分别粘贴到Code ACode B字段即可。代码是以2of3-开头的长字符串。代码内部的空白和换行会被忽略,因此在笔记应用里折行显示的代码也没问题。这对应parseShare开头对输入执行的input.replace(/\s+/gu, ""),见 shamir.ts。

错误信息含义

恢复出错时,2of3 会说明发生了什么。常见的有:

  • "These two cards are from different sets. Match the ID on both cards."(这两张卡来自不同套装,请匹配两张卡上的 ID。)你上传的两张卡由不同秘密生成(ID 不同)。检查每个槽位下方的ID A1B2C3D4一行,两者必须一致。如果不一致,找到属于正确套装的那张卡。对应combineShares中的 ID/长度不一致检查(shamir.ts)。
  • "Use two different cards from the same set."(请使用同一套装中两张不同的卡。)你上传了同一张卡两次,或同一张卡的两份拷贝(例如纸质卡和它的 PNG)。你需要同一套装中两个不同的卡号:1 和 2、1 和 3、或 2 和 3。对应 shamir.ts。
  • "That code does not look like a 2of3 share."(该代码不像 2of3 的 share。)粘贴的文本不是 2of3 代码。真正的代码总是以2of3-开头。对应 parseShare 的前缀检查。
  • "That share looks incomplete."(该 share 看起来不完整。)"This share was cut off."(该 share 被截断了。)代码末尾缺少字符。通常意味着复制粘贴丢了代码尾部,或代码换行后漏掉了最后一行。对应 parseShare 的长度校验 与 payload 长度检查。
  • "These shares did not reconstruct a valid secret."(这些 share 未能重建出有效秘密。)两张卡解析正常,但没有组合出原始秘密。通常意味着至少一张卡损坏(二维码误读、文本代码被改过、或打印质量差)。试试另一对卡片;一套三张卡共有三种配对。对应 combineShares 的校验和比对失败。
  • "Could not read that image."(无法读取该图片。)"Could not read that QR code."(无法读取该二维码。)上传的图片不是可识别的二维码。尝试更清晰的照片、更紧的裁剪,或上传原始 PNG 而非截图。

替换或轮换一套卡片

如果需要更改底层秘密(例如轮换了密码),请把它当作一套全新的 2of3 卡:

  1. 用新秘密生成一整套三张新卡。
  2. 把新卡分发到原来的三个地方。
  3. 销毁旧卡(碎纸销毁纸质卡、删除 PNG、从笔记中清除文本)。

不要"以防万一"保留一张旧卡。一张旧卡和别处的另一张旧卡组合,仍然能恢复旧秘密——而这恰恰是你不再希望它可恢复的东西。正如 FAQ 所说,2of3 没有"就地编辑某一张卡"的概念,因为数学上三张卡通过同一个逐字节随机系数关联在一起,见 docs/docs/2of3/faq.md。

卡片上到底是什么

理解卡面内容有助于信任这套工具,也为日后自建恢复工具提供了依据。

文本代码格式

每张卡携带一份 share,编码为一小段文本代码(同一张卡上同时渲染为二维码,便于手机拍摄)。代码形如:

2of3-AQEA...

2of3-前缀让读者一眼识别代码,也让 2of3 能对粘贴的非 share 文本给出明确报错("That code does not look like a 2of3 share.")。前缀之后是 base64url 编码的字节负载,包含 10 字节头部加 share 数据。

Share 格式字段

从开头读起,负载结构为:

字节字段含义
0Version(版本)当前格式恒为2。恢复同时支持版本 1 创建的卡片。
1Card index(卡片编号)123,即恢复时用于合并 share 的"x 值"。
2-3Length(长度)秘密字节数的 16 位大端长度。恢复时两份 share 必须一致。
4-9Random ID(随机 ID)crypto.getRandomValues生成的 6 字节随机值,每套卡生成一次。同一秘密的三张卡共享此 ID。
10+Share data(份额数据)秘密字节的 share,后随 4 字节校验和。校验和与秘密受相同的"两张卡"门限保护。

以上字段与 How it works 的 Share format 一节 完全对应,并在源码 shamir.ts 的 splitSecret 头部构建 与 parseShare 的解析 中逐字节落实。卡片 UI 上显示的ID A1B2C3D4是人类可读指纹,即 base64url 编码 ID 的前 8 个字符(parseShare(value).id.slice(0, 8).toUpperCase(),见 index.tsx 中的 shareFingerprint 与 export.ts 中的指纹渲染)。

格式兼容性

早期版本创建的是版本 1 卡片,它公开存放校验和,允许单张卡对低熵秘密进行猜测确认。版本 2 把校验和保护在两张卡门限之后。恢复同时支持两种版本(如果你持有更老的离线恢复页面,请生成一套新卡以获得最新版,或直接在 2of3.ente.com。

离线恢复文件:十年后 2of3.ente.com 不在了怎么办

点击Download all cards时,2of3 会在三张 PNG 之外附带一个额外文件:2of3-recovery.html。这是恢复流程的离线副本,里面包含:

  • 与 2of3.ente.com 相同的恢复界面(独立 HTML 页面)。
  • 完整的 Shamir 合并逻辑,以内联 JavaScript 写就。
  • 一个内嵌的二维码解码器,可以本地解码纸质卡的照片。
  • 零网络请求,不依赖 Ente、2of3.ente.com 或任何 CDN。

双击即可打开,任何现代浏览器都能运行。用这个文件恢复与在 2of3.ente.com 上恢复完全一致:放入两张卡(图片或文本)、点击Recover secret、复制结果。

这个文件回答了"十年后 2of3.ente.com 消失了怎么办"的问题:答案是什么都不会发生。打开随卡片一起存储的 HTML 文件即可恢复。即使 Ente 公司不在了,这个文件也不需要从我们这里获取任何东西——它是几百行可审查的 JavaScript,行为与 2of3.ente.com 完全一致。从源码看,该文件由createOfflineRecoveryHtml生成,它把离线二维码解码器源码(来自qr-raw/decode.jsqr-raw/index.js,经stripSourceMapComment等处理后去依赖化)内联进globalThis.__twoOf3DecodeQR,并内联了与线上完全相同的 GF(256) 与合并逻辑,见 export.ts 中的 createOfflineRecoveryHtml 与 offline-qr-source.ts。

[!IMPORTANT]

把离线恢复文件当作备份对待。至少在某处存一份耐久的副本:U 盘、备份盘,或与卡片一起放进保险箱打印出来。卡片是秘密本身,恢复文件是打开它们的工具。

由于这个文件是自包含的纯 JavaScript,你还可以把它当作验证目标:通读、静态分析、或与开源仓库中的实现对照,来确认它确实做了它声称的事。这里没有需要信任的远程服务。

背后的数学:Shamir 秘密共享简述

2of3 建立在Shamir 秘密共享之上,这是 1979 年提出的经典密码学方案。2of3 使用其最简单而有意义的形态:把秘密拆成三份 share,任意两份即可还原原秘密,而单份 share 除字节长度外不泄露任何信息。

直觉模型是一条经过两点的直线:任取一条穿过 y 轴的直线,y 截距就是"秘密",斜率是随机数、用完即弃。选三个不同的 x 值(如 x = 1、2、3),算出直线上对应的 y 值。每个 (x, y) 对就是一份 "share":

  • 给某人一份share(直线上一个点),他能画出无穷多条经过该点的直线,对 y 截距一无所知。
  • 给某人任意两份share(两个点),有且仅有一条直线经过它们,可以算出 y 截距,即秘密。

如果在普通数域上做这件事会泄露信息(例如大秘密的 share 尺寸可预测),因此 Shamir 方案在有限域上做算术。2of3 使用GF(256)(含 256 个元素的有限域),其便利之处在于每个字节都是合法元素,从而可以逐字节拆分秘密,且每份 share 与秘密等长。

对于 2of3 使用的 (k=2, n=3) 方案,逐字节的数学可以完整写出:

  • 对每个秘密字节s,2of3 挑选一个均匀随机的字节r
  • Share 1 存放sXORr
  • Share 2 存放sXOR (r × 2 in GF(256))。
  • Share 3 存放sXOR (r × 3 in GF(256))。

GF(256) 乘法使用标准的 AES 多项式(0x11b)。上述三份中的任意两份加上它们的编号("x 值"1、2、3)即可逐字节恢复s,从而得到完整秘密。单独一份 share 则是sXOR(某个随机字节),与随机数不可区分。

这些公式可以在源码中逐行对应:splitSecret 用gfMul(coefficient, 2)gfMul(coefficient, 3)生成三份 share,gfMulGF_POLY = 0x11b做多项式约减(shamir.ts);combineShares 用gfDiv(基于费马小定理的gfPow(value, 254)求逆,shamir.ts)做拉格朗日插值合并。校验和是 32 位 FNV-1a 哈希(初始值0x811c9dc5、乘数0x01000193,见 checksumBytes):拆分前追加到秘密之后,合并后再计算比对——校验和同样被门限拆分保护,因此单张卡无法用它试探秘密。

[!NOTE]

2of3 特意只实现 2-of-3 这一种情形,以求清晰与简单。同样的方案也支撑着 Ente 账户版的 Legacy Kit:它在同样的 2-of-3 数学之上,叠加了与 Ente 账号绑定、可撤销的服务器中介恢复流程。如果需要通用的 k-of-n Shamir 共享用于研究项目,可以去看 SLIP-0039 等标准;2of3 的使命是让 2-of-3 对普通人零摩擦,而不是做一个通用密码学库。

隐私边界与典型问题

  • 秘密会发给 Ente 吗?不会。拆分与恢复都在浏览器内完成,没有账号、没有登录,你的秘密不会离开页面。下载的离线恢复文件也完全离线工作。页面本身作为 ente.com 上的普通页面托管,存在常规的服务器请求日志(页面加载的 IP),但你输入的秘密与生成的卡片完全在设备本地处理,见 FAQ 的隐私章节。
  • 单张卡能泄露秘密吗?不能。单张卡的 share 数据在数学上无害,看起来是随机的——因为它确实就是:受保护的数据与逐字节均匀随机的字节做 XOR。它只暴露秘密的字节长度,不暴露任何内容,见 FAQ。
  • 和密码管理器有何区别?密码管理器用于日常使用的秘密;2of3 用于密码管理器自身所依赖的那一两个秘密(主密码、恢复密钥、助记词),两者互补而非替代。2of3 也不是存放密码的地方——它是一次性的单秘密拆分,没有 vault、没有同步、没有多条目概念,见 FAQ 与 FAQ。
  • 聊天应用破坏了链接或文本会怎样?如果通过聊天应用发送文本代码导致换行、混入不可见字符或自动格式化,接收方可能拿到损坏的代码。2of3 忽略普通空白与换行,折行代码没问题;但如果应用把字符变成"智能引号"或剥离了2of3-前缀,代码将无法解析。更稳妥的做法是分享卡片的PNG 图片、原始 PNG 附件,或通过不修改消息体的端到端加密渠道发送,见 FAQ。

深入阅读

  • 2of3 总览:2of3 是什么、何时使用。
  • How it works:Shamir 秘密共享、share 格式、离线恢复文件与 QR 解码器。
  • FAQ:关于安全性、存储与边界情况的常见问题。
  • 开源实现:web/apps/twoof3 下的 Next.js 应用,核心逻辑集中在 utils/shamir.ts(GF(256) 与拆分/合并)、utils/export.ts(卡片渲染与离线恢复文件生成)、utils/qr-code.ts(二维码编码,ECC 等级medium、边框 4 模块)。

按照本文的完整流程操作一遍——拆分、分点存储、测试恢复、离线文件备份——你就拥有了一个不依赖任何在线服务、数学上可验证、十年后依然可用的秘密恢复体系。

【免费下载链接】ente💚 End-to-end encrypted cloud for everything.项目地址: https://gitcode.com/GitHub_Trending/en/ente

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 20:22:45

Android开发中Intent的全面解析与应用实践

1. Intent 的本质与核心作用 在移动应用开发领域,Intent 是 Android 系统中最重要的通信机制之一。它就像现实世界中的"快递员",负责在不同组件之间传递信息和执行操作。我从事 Android 开发十年来,Intent 的使用贯穿了几乎每一个功…

作者头像 李华
网站建设 2026/9/10 20:21:12

聊天系统-flask WebSocket SQLite

本项目为前几天收费帮学妹做的一个项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。 一、项目描述 一个基于 Flask WebSocket SQLite 构建的轻量级实时聊天平台,支持群聊…

作者头像 李华
网站建设 2026/9/10 20:18:45

AI如何优化Oracle数据库安装与配置流程

1. 项目概述:AI如何颠覆传统Oracle安装流程第一次在Windows Server上安装Oracle 11g的经历至今记忆犹新——从官网下载2GB的安装包,配置环境变量,处理各种依赖冲突,到最终看到"Database Configuration Assistant"完成提…

作者头像 李华
网站建设 2026/9/10 20:17:38

卫星移动、小区不动:NTN中的Mapped Cell ID信令与测试实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 20:15:42

Qbot 如何本地部署:免费开源 AI 量化交易平台的完整指南

Qbot 如何本地部署:免费开源 AI 量化交易平台的完整指南 【免费下载链接】Qbot [🔥updating ...] AI 自动量化交易机器人(完全本地部署) AI-powered Quantitative Investment Research Platform. 📃 online docs: https://ufund-me.github.io…

作者头像 李华