6.4 安全投票与暗标拍卖:匿名与可核查并存 本节摘要:投票与拍卖是最苛刻的证明现场:参与方既要求保密(投票匿名、出价保密),又要求结果可核查到每一票每一标。本节拆解端到端可验证投票的三段公理、选票合法性证明与混洗证明的角色,用一段同态计票演算落地,并延伸到暗标拍卖的承诺与区间证明。承接 6.3,通往第 7 章前沿。 一段历史带来的苛刻需求 纸票时代的选举信任建立在"流程可围观"上:公开唱票、监票员在场、箱子当众封存——每一步都靠肉眼盯。搬进数字世界后,肉眼失效了:电子投票盒是黑箱,唱票软件无法围观,于是出现一个看似无解的需求组合——票要匿名(任何人不能知道我投了谁),结果要可核查(任何人能验证每票被如实计入)。传统密码学只能二选一:加密保匿名则计票变成信任计票方,公开计票则匿名不保。
本节摘要:投票与拍卖是最苛刻的证明现场:参与方既要求保密(投票匿名、出价保密),又要求结果可核查到每一票每一标。本节拆解端到端可验证投票的三段公理、选票合法性证明与混洗证明的角色,用一段同态计票演算落地,并延伸到暗标拍卖的承诺与区间证明。承接 6.3,通往第 7 章前沿。
纸票时代的选举信任建立在"流程可围观"上:公开唱票、监票员在场、箱子当众封存——每一步都靠肉眼盯。搬进数字世界后,肉眼失效了:电子投票盒是黑箱,唱票软件无法围观,于是出现一个看似无解的需求组合——票要匿名(任何人不能知道我投了谁),结果要可核查(任何人能验证每票被如实计入)。传统密码学只能二选一:加密保匿名则计票变成信任计票方,公开计票则匿名不保。证明系统与同态加密的组合,恰好把这个"既要又要"变成工程现实,本节把它拆开看。
成熟方案的信任链条被切成三段,每段对应一条可独立核查的公理。投得如所愿(cast as intended):投票设备可能撒谎,选民需要手段确认加密选票里装的确实是自己的选择——常见做法是挑战式抽查(设备现场"演算"加密过程供选民比对)或返回可人工比对的验证码。记得如所投(recorded as cast):选票进入公告板后,任何人可以核对"我选票的指纹确实在榜上",改动或丢弃选票会被选民本人发现。算得如所记(tallied as recorded):最终计票结果必须能从公告板上全部加密选票可验证地推导出来——这最后一段就是证明系统的主场。
算票段的核心构件有两件。选票合法性证明:每张加密选票附带一份证明"密文里装的确实是候选集里的合法选项"(比如恰好一票而非三票),防止恶意密文污染计票;这份证明是零知识的——它不揭示投了谁,只保证格式合法。混洗与混洗证明:计票前把全部密文打乱重排并部分解密,混洗方必须提供证明"重排后的名单与原名单是同一批元素的置换"——既切断选票与选民的关联(匿名性),又保证没人偷偷丢弃或替换选票(可核查性)。两件构件合起来,"匿名"与"可核查"才真正并存。
算得如所记最优雅的实现是同态聚合:加密操作满足"密文相乘等于明文相加",于是不用打开任何单张选票,把全部密文连乘就能得到"各候选人总票数"的加密值,最后对聚合值一次性解密。跑一个玩具版本,把全流程走通:
# 同态计票玩具演示:指数式 ElGamal,小结果空间可暴力破解 P = 10007 # 玩具素数域 G = 5 # 生成元 sk = 2024 # 计票方的解密私钥(选举后销毁) pk = pow(G, sk, P) # 公钥:全体选民可见 def encrypt_vote(choice, r): # 加密一张选票:密文 =(G^r, G^choice 乘 pk^r) return (pow(G, r, P), pow(G, choice, P) * pow(pk, r, P) % P) import secrets ballots = [1, 0, 1, 1, 2, 2, 1, 0] # 八位选民的选择(候选 0/1/2) ciphertexts = [encrypt_vote(v, secrets.randbelow(P)) for v in ballots] # 每张密文旁还需附"合法性证明":密文加密的确实是 0/1/2 之一(零知识),此处从略 # —— 同态聚合:把全部密文按分量连乘 —— A = 1 B = 1 for a, b in ciphertexts: A = A * a % P B = B * b % P # B 此刻等于 G^(总票数和) 乘 pk^(随机数和),单票选择全部隐没在指数里 # —— 一次性解密聚合值:用私钥剥掉 pk 项,再暴力搜指数 —— B_over = B * pow(pow(A, sk, P), P - 2, P) % P # B 除以 A^sk = G^(票数总和) total = None for v in range(0, 100): # 候选人数与选民规模有限,指数空间小 if pow(G, v, P) == B_over: total = v break print("全体票数总和:", total, "(应为", sum(ballots), ")") # 真实系统按候选分桶聚合,逐候选解出票数; # 解不出(不在小范围内)说明有人投了非法票——合法性证明在此兜底
演算的最后一步藏着一个耐人寻味的细节:小结果空间的暴力搜索在这里是特性而非缺陷——票数本来就有限,指数搜得回来;非法密文因为搜不到对应指数而自动暴露。单票层面呢?单票的指数空间是候选数(很小),任何人都能解——所以真实系统里单票选择必须藏在指数式加密的指示位设计中,并且合法性证明必须强制到位。这个细节说明:投票协议的安全性是各构件咬合出来的,任何一件单独拿出来都有缝。
拍卖现场的保密需求更直白:出价保密、规则可查。承诺阶段对应 4.1 的第一幕——各投标人把出价封进承诺上交;开标阶段揭示承诺比价。两处要用证明加固:承诺时附区间证明(出价落在合法区间、账户保证金确实覆盖该出价额——不然开着天价承诺不认账就闹剧了);存在底价约束时附比较证明(我的出价高于底价,但不披露高多少)。对于"第二价格拍卖"(次高价成交)这类需要排序的规则,还可以让所有投标人对自己的出价做"我是不是最高"的比较证明,全程无人披露具体数额而胜者自明。招标系统的公开可审计诉求与选举同源——谁参加、承诺是否唯一、开标是否如实,每一段都要有独立证据。
投票现场还有组织层难题(胁迫抵抗、远程投票的终端安全)超出密码学边界,这里按住不表。到此四个现场走完,第 7 章抬头看:证明系统的下一代、通用虚拟机、抗量子与工具链——每项前沿都对应现场的某个未解成本或信任项。
问:选民怎么确认自己那票没被改?这就是"记得如所投"公理的日常操作:投票完成后系统返回一张可自查的回执(选票的指纹或追踪码),选民随时可在公告板检索确认它在榜;更高强度的方案允许挑战设备——随机要求投票机"现场证明"加密内容与屏幕选择一致,抽查被发现的概率让作弊设备不敢赌。注意回执设计的红线:回执必须不能当作投票内容的证明出示给他人,否则贿选者拿它验收选票,匿名性反而成为受贿工具。
**问:计票方私钥丢了或被胁迫交出怎么办?**成熟方案不用单把私钥:解密权拆成门限份额分给独立的受托人,凑齐门限数量才能解开聚合值;任何单一受托人被收买或失联都不影响流程也不威胁保密。份额的生成、分发、销毁流程要写进选举规程并全程留痕。
**问:拍卖里"承诺了又拒不揭示"怎么处理?**开标阶段设计惩罚与保证金机制:承诺时按出价上限锁定保证金(可用区间证明证明"保证金覆盖承诺值"),拒不揭示者没收保证金——承诺的绑定性让赖账行为有据可罚。工程上还要处理揭示阶段的通信失败(出价人掉线),常见做法是揭示窗口加宽限期、或允许委托第三方持揭示材料代为揭示。