一家服务说:“证明你知道一个秘密,且本次公开金额没有超过公开限额;但不要在验证消息里直接发秘密。”听起来像要找一种能把一切都藏起来的“魔法签名”。实际上必须先写清:被证明的关系是什么、哪些输入留在验证者眼前、哪些是秘密见证,以及哪些业务事实根本没有进入关系。若“账户里确实有这么多余额”没有写进关系,再漂亮的证明也回答不了余额问题。

三个对象先分开

承诺是将某个值绑定进可核查的摘要或群元素;验证通常需要可信的承诺值以及证明者提供的打开数据。只对低熵订单号做 SHA-256 并公开摘要,并不能自动藏住订单号:旁观者可以列举所有可能值重算摘要。需要隐藏性时要考虑随机盲化、高熵秘密以及具体方案的安全假设;“只有散列看不出原文”不是普适保证。

关系定义验证者想确认的判断,如“我知道一个 secret,其 Poseidon 摘要等于公开 commitment,并且公开 amount <= limit”。secret 是本关系的见证,commitment、amount、limit 是公开输入。若 amount 本身公开,零知识性质也不能将 amount 隐藏;若哈希秘密只有四位,公开 commitment 仍可能被猜出来。证明是针对冻结关系及公开输入由特定证明系统生成的可验证字节,验证者不需要直接收到 secret。这个实验没有绑定“该 secret 对应此笔余额”或“订单已交付”;即使真证明通过也不能外推出这些事。

1
2
3
4
5
6
7
8
一次性秘密 secret ── Poseidon(secret) ──► public commitment ┐
公开 amount, limit ───────────────┐ │
约束:amount <= limit 受限关系电路 │
↓ ↓
Prove(见证、公开值、证明密钥) ──► proof
Verify(验证密钥、公开值、proof) ──► 接受 / 拒绝

电路未要求:订单是真订单 / 密钥属于这名用户 / 余额够用 / 数据已发布

examples/cryptography/34_private_order.circom 用成熟 Circomlib 的 Poseidon(1)、Num2Bits(64) 与 LessEqThan(64) 组成这条关系。前者核对 secret 的承诺,两个公开数字先被分别限定到 64 位范围,再检查 amount<=limit;若只写比较器却忘了限定输入域,模素数的环绕会损害预期语义。电路没有把秘密和 amount 联结。若删掉限额约束,原本因 101 > 100 无法生成合法见证的请求会变成可证明:漏洞不在 Groth16 的群运算,而是关系漏掉了业务规则。若删掉承诺相等检查,随便选秘密也可满足余额比较。

真实证明与真实失败,不用日志扮演证据

examples/cryptography/34_prove.mjs 在临时目录编译电路成 R1CS/WASM,生成仅本次使用的随机 secret 与其公开 Poseidon 承诺,以冻结的 snarkjs Groth16 创建 proving key 和 proof,用导出的验证密钥和公开输入实际运行 verifier。正例返回 real_groth16_proof_verified=true。保留 proof 却把公开 amount 改一单位,真实 verifier 拒绝;把输入设为 101、限额 100,见证生成/约束检查拒绝。这不是 return amount <= limit 冒充 ZK:正常流程真正编译、生成、验证了 Groth16 proof。

先按仓库内 examples/cryptography/README.md 完成环境准备;以下命令从仓库根目录执行。

1
2
node examples/cryptography/34_prove.mjs
node --test examples/cryptography/34_prove.test.mjs

实验选择本机生成的 单人一次性 Powers of Tau 与电路设置,随后直接删除所有临时材料,不记录私有见证。单一操作者设置含有可信设置假设的缺口:不能把运行成功说成经过多方仪式的生产安全。公开输入全部留在验证者手里;秘密本身不作为公共输入,但电路、随机源、参数与低熵猜测可能破坏实际隐私。正例和两个失败对照在冻结工具组合中通过,不代表经过安全审计、链上集成、永久可用性或商品支付。正式零知识安全结论需具体方案假设与实现核验。

练习及答案

画图题: 把 secret、commitment、amount、limit、验证密钥、proof 分到证明者或验证者一侧。服务希望“证明这笔订单也已付款”,可只给限额比较再改证明参数实现吗?

答案: secret 仅在证明者生成见证时使用;其余公开输入与验证密钥、proof 给验证者。这个电路只证明持有承诺原像及公开数值关系,不含扣款状态;改证明参数无法补不存在的约束。需要定义余额及签名授权、状态转移和可信数据输入,再重新审查完整关系;即便证明这样的状态变化也不能自动说明现实发货。

实验变更题: 不重算 proof,仅将 public.json 里的 amount 改为 43,和改 input.json 的 amount 为 101 分别在哪一步失败?若取 secret=5,保留 Poseidon 承诺,即使 proof 验真还可能泄露什么?

答案: 已有 proof 对绑定的公开 amount 有约束,验签器拒改后的公开值;101 > 100 不满足电路,见证生成或 R1CS 检查就失败。对于只在极小候选空间的 secret,旁观者可枚举 0、1、2…,对比公开 Poseidon commitment 找回 5;形式上的证明成功不把低熵输入变得不可猜。

资料与衔接