沿盲求值学习路线,依次完成哈希到曲线、OPRF和固定公钥VOPRF。三份结果分别回答:字节变成哪个点、客户端得到哪个函数值、这个值是否通过既定公钥的验收。
下载标准库参考程序,运行 python foundation-oprf-check.py;结果只写标准输出,python -O仍执行显式检查。全部钥匙、盲因子与证明随机数都是公开离线样例。程序包含数据相关分支和Python大整数运算,不用于生成秘密钥匙或服务真实请求。
任务一:逐层复算字节到点
固定P256坐标模数p和群阶q,以RFC9380 Appendix J.1.1的DST QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_ 处理空串。交付96字节XMD扩展、两个48字节段模p后的域元素、Q0/Q1以及最终和P。不能把模q的挑战标量放到这两个域元素的位置。
正文已给完整u0/u1和点;最终P的规范压缩编码必须为
032c15230b26dbc6fc9a37051158c95b79656e17a1a920b11394ca91c44247d3e4
补交两种结构迁移:
- 将DST追加ASCII的
-other,得到另一对域元素与点;说明输入消息虽未变,使用的用途域已经改变 - 只返回Q0,检查它仍在曲线上,但不能再声称执行了RO套件规定的两次映射和相加
再检查SSWU例外。除u=0外,以下两个数互为模p相反数,平方都等于1/10,它们也使Zu²=-1和分母t²+t=0:
95d527d249c8dc5cadbf4c70bb59aaab72c14fffbad5622bd147b86a639ec6d9
6a2ad82cb63723a45240b38f44a655548d3eb001452a9dd42eb847959c613926
三次都应取x=B/(ZA),再按输入奇偶选y;只检查 u==0 的实现应在这项任务中暴露遗漏。
任务二:同一输入,两种模式和两份盲化
用公开Seed=a3重复32字节,KeyInfo为ASCII test key,输入为单字节00,盲因子固定为
3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364
依照RFC9497分别构造模式0和模式1的context。程序从DeriveKeyPair第0次候选就得到非零钥匙;交付两份完整私钥、公钥、盲点、响应点、去盲点和Finalize输入。mode0输出为
a0b34de5fa4c5b6da07e72af73cc507cceeb48981b97b7285fc375345fe495dd
mode1输出为
0412e8f78b02c415ab3a288e228978376f99927767ff37c5718d420010a645a1
它们不同是正确行为:模式字节参与哈希域,种子相同不意味着同钥匙或同输入点。模式1还必须完成下一任务才能交出函数值。
在模式1中把盲因子改为r+1,线上点改变为
U' = 03eebc5884f54729bdb84709ce101177637658395495a52ff6c12f3d589f3ed184
V' = 03b2d3476296e83befd87a88e62e8bb7a72b80d049f17870a17d216b685e40507a
去盲和输出仍相同。请用双射证明解释新鲜盲化为何隐藏请求,再区分以下两句:这次请求是均匀非单位点;客户端无法用一次查询得到另一个输入的函数值。第二句并不由第一句推出。
任务三:重建一份单元素证明
固定模式1公钥
03e17e70604bcabe198882c0a1f27a92441e774224ed9c702e51dd17038b102462
使用正文给出的U/V及64字节证明,逐项交付Seed散列、组合系数d、M=dU、N=dV、重建T2/T3和184字节挑战输入。证明随机数的公开测试值是
f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
要求验证器以c/s重建两点,不能读取这份随机数后假装完成验证。比较实际复算挑战与证明c,只有一致才去盲。输出含完整证明和两个临时点,既能与RFC公开向量逐字比较,也能定位差异。
保持公钥不动,改用另一钥匙k+1回答同一U,并为新公钥生成合法证明。固定原公钥的验证必须失败,且没有Finalize值;将公钥也换成新公钥后可以通过。解释后一个结果认证的是哪个函数,以及为什么公钥来源必须在协议外先被确定。
任务四:正确的DLEQ也救不了错误输入映射
仅在这一反例中,将输入点换成公开的h(x)G,h使用同一HashToScalar并取实际非零的样例。查询消息ASCII first 一次,去盲得到kh(first)G,再乘h(first)的逆元,恢复的公钥必须等于上面的K。
对从未查询的ASCII消息 unqueried,用h(unqueried)K生成点和Finalize,程序输出
b77e49f1703b13c7d21d707e97a3fe0fac121e5cfa76338e780826d0b61c2e8c
服务器可以为那一次错误映射的请求响应给出完全有效的DLEQ证明,参考器也确实验证通过。它只证明两个点之间使用了同一钥匙,没有证明上层选择的HashToGroup具有需要的安全性质。请交付这份有效证明和未查询消息的离线值,不能只口头说“可能不安全”。
然后在正常模式1的两个不同盲点上复用同一个公开证明随机数。得到(c,s)、(c',s')后计算(s−s')/(c'−c)模q,恢复值必须为
ca5d94c8807817669a51b196c34c1b7f8442fde4334a7121ae4736364312fca6
这是公开教学钥匙。两份证明单独通过不等于随机数复用安全。
拒绝记录也是交付的一部分
程序真实核对零盲因子/私钥/证明随机数、bool标量、65535字节输入、空/超长DST、截短点、单位元编码、横坐标p、非曲线横坐标、标量q、错本地请求和错公钥。65534字节输入仍可通过;标量c/s为零本身不是编码错误,但全零证明重建单位元而被当前受限接口拒绝。
对极少发生的d=0,使用测试专用散列桩让它实际出现,确认返回退化错误。桩只用于走分支,不参与标准向量;失败后不能把d改成1再继续。本接口采用单元素失败关闭规则,不把它描述为RFC全批处理实现。
最后写清三项结论各自的前提:SSWU候选证明保证曲线方程,均匀盲化模拟保证指定消息视图的输入隐藏,DLEQ挑战界保证指定理想挑战模型中的错误关系难以接受。完整OPRF伪随机性、现实哈希和侧信道仍有各自的假设与实现责任。