From Interaction to Independence: zkSNARKs for Transparent and Non-Interactive Remote Attestation
2024-3 Network and Distributed System Security (NDSS) CCF-A
论文简记:该论文针对传统RA方案的交互性和过程不透明,引入基于Merkle Tree的zkSNARK和Poseidon hash
方案具有:透明、全局挑战、与平台无关、防DoS攻击、信任分散(去中心化)等优势
🔁 研究内容
💡 该论文的Tag
zkSNARK、Poseidon hash、Merkle Tree、blockchain、smart contract
🧩 研究意义(现实背景)
Remote attestation:用于验证网络设备完整性,应用于安全启动验证、组织内部访问控制、数据权限等领域
e.g.GPT:假设有一个智能门锁
- 验证固件完整性:当智能门锁上线时,它将生成一个报告,证明它的固件未被修改。
- 报告可信状态:通过安全硬件签名,该报告发送到验证服务器。
- 允许访问:验证服务器确认门锁运行的是官方固件后,允许其接入网络。否则,拒绝连入网络
⚙️ 研究的问题
验证者:制造商 证明者:设备
传统RA中,验证者拥有设备内容特权信息,验证者向设备发出随机挑战,设备生成响应,验证者根据特权信息推测预期响应并和设备响应比对,如果一致,则设备可信,否则,存在异常。
显然,上面是交互式,随着设备数量增多,交互式协议存在可拓展性问题
此外,特权信息一般由设备厂商掌握,用户无法验证,即RA不透明
问题总结为:如何在远程设备上进行透明且无交互的远程认证
💧 解决该问题的方案演变、各自优点和局限性,为什么要提出该论文
使用区块链技术、代理验证器来增强传统RA的可扩展性问题,但没有突破交互性质和特权信息的特殊访问权限的限制
有些相关工作探索了非交互式RA协议,依赖于专用代理或者私有Hyperledger全节点,但成本高昂且服务器崩溃\Rightarrow整个RA协议崩溃
总结为:以往RA技术依赖于交互式通信、设备特权信息的预先共享或者由可信方验证RA请求,文章提出zRA方案,消除实时交互,允许不询问可信方(制造商)的情况下对设备认证。
👩🏻💻 论文中使用的前人技术、方案
- 使用zkSNARKs(零知识简洁非交互式知识论证)生成可证明且可扩展的证明,即不受设备和生成的证明数量影响
- 引入Commitment Schemes实现透明验证 attestation claim的正确性
- 使用MPC协议在利益相关者之间分配设置,增强 Distributed trust
- 使用Poseidon hash作为哈希函数,提高zkSNARK效率
- Merkle Tree
- 利用区块链的透明、去中心化特性,实现Global challenge和证明透明
🔬 方案设计
-
设置阶段:制造商离线执行此阶段,为每个设备计算一组未来的响应,并构建一个包含所有设备响应的Merkle树。
制造商预先为每个设备生成m个挑战,构成一个Merkle Tree,n个设备的Merkle Tree Root Hash 构成一个新的Merkle Tree。Merkle Tree可以公开访问(但不包括challenge的顺序)这保证,每个设备的每个证明的路径唯一,用于ZKP证明阶段
-
更新全局挑战:制造商定期将最新的挑战发布到区块链上,以便设备可以获取最新的挑战。
-
认证阶段:设备查询区块链以获取最新的全局挑战,计算认证证据,并使用zkSNARKs生成证明,然后将证明提交到区块链上。
-
验证阶段:验证者只需检查提交的证明的有效性,无需特权访问授权数据。
🤔 个人总结
📜 论文中的那些设计/语言值得总结?
- 论文中,制造商定期发布挑战,制造商是中心化的,有单点故障问题(制造商崩溃/挑战序列被揭露),论文让所有利益相关者参与阈值挑战生成仪式,将Setup阶段的Distributed Trust,更灵活、实用、适应性
- 传统是每个设备与制造商服务器通信,论文是制造商将挑战发布到区块链,设备通过区块链API检索挑战,实现“证明与设备数量无关“
只是把通信成本转移到了区块链,但区块链去中心化,无单点故障、防DoS攻击。可参考思路、写法
论文代码
zkRA repo:github.com/zero-savvy/…
代码主要基于Circom和Snarkjs两个库,可以阅读【circom与snarkjs教程】初步入门
这里简要说明:
Circom:将计算问题转化为算术电路,导出
.r1cs:描述电路的逻辑和约束,也就是电路的“数学公式”.wasa:电路的运行程序,读取数据,按照.r1cs计算,输出结果witness(.wtns)snarkjs:看作是一个工具链,基于JavaScript语言根据算术电路,来实现zkSNARK的“特性”(简洁性、非交互等)
- 基于
.r1cs文件生成证明密钥和验证密钥,用于证明的生成和验证- 基于密钥和电路生成零知识证明
- 可将验证逻辑导出为
xxx.sol,用于与区块链集成
文件结构
benchmarking circom doc js python README.md
主文件夹简要说明
- README.md: 项目的简略文档;
- circom文件夹:存放Circom电路文件
- python文件夹:定义一些脚本操作,比如和以太坊的交互操作、打印Merkletree操作等
- js文件夹:定义密钥和设备管理的脚本、导入工具包、设置项目依赖等
- benchmarking文件夹:存放性能测试相关文件,如生成的ZKP,公共参数等,每个文件夹是一个“成品”,可以作为最终目标
根据最开始的介绍可知,重点在于js/和circom/文件夹
circom/ 文件夹
circom相关知识:Circom 是一种将计算问题编写为“数字电路”的编程语言,用于定义零知识证明电路。一般每个
.circom文件定义一种运算操作(+、*、hash、sign)的电路逻辑Circom 2文档:docs.circom.io/
用于存放Circom电路文件,这些电路是零知识证明系统的核心组件。
-
circuits/tornado-core/circuits/merkleTree.circom: 实现Merkle树验证的电路,用于验证Merkle路径的正确性。
-
circomlib/:circuits/poseidon.circom: 包含Poseidon哈希函数的具体实现细节,供其他电路引用。
-
zRA.circom:核心电路文件- 定义zkSNARK电路,负责生成证明的电路逻辑
-
安装circom环境后,生成电路表示文件 circom zRA.circom --r1cs --wasm --sym -o build - 提供输入文件(比如benchmarking文件夹中的good_input.json),再通过snarkjs工具,生成证明
-
attest.sol- 是论文作者写的一些逻辑判断、权限认证
- 部署到以太坊用于验证证明、定期发布全局挑战、管理设备的Merkle树
-
verifier.sol-
通过snarkjs工具的
generateverifier命令生成 -
证明验证的具体实现,没必要看,看懂
zRA.circom即可
相关命令参考官方文档、参考博客或下文第8节,这里不赘述
-
js/ 文件夹
包含协议设置阶段的代码,应由制造商执行,负责项目的设置、设备密钥生成、挑战生成以及Merkle树的构建等功能。
manufacturer.js: 负责生成设备的私钥、公钥和地址。setup_phase.js: 主要用于设置阶段,包括设备数量的输入、生成挑战、构建Merkle树等。findPath.js: 用于在Merkle树中查找特定叶子的路径,生成Merkle路径和索引。utils.js和tool.js: 提供辅助函数device.js:无用
python/文件夹:
用于与已经部署的合约进行交互
device.py:通过web3.py与以太坊网络交互,使用私钥构造并签名一笔“attest”交易,然后将其发送到指定的合约地址。posi.py:根据论文eprint.iacr.org/2019/458.pd…中的参数要求,定义Poseidon哈希函数merkle_tree.py:使用Poseidon哈希函数实现Merkle tree,而非Keccak-256、SHA-256。模数p为256bit
运行流程
-
制造商执行js文件夹下
setup_phase.js- 输入设备数量k和认证频率t
- 调用
manufacturer.js的createDeviceKeys()生成k个设备密钥、n个未来待发布的挑战序列->存储在chanllenges.json - 每个设备对应n个挑战、n个响应,通过poseidon哈希函数绑定在一起,作为Merkle Tree的叶子节点,一个设备对应一个
devMT - 根据各个设备的
devMT的根节点,构建主Merkle Tree。 - MerkleTree文件在
devMT_files/
-
制造商编写
circom/zRA.circom通过circom编译、snarkjs导出为attest.sol合约,部署到区块链 -
设备本地编译
zRA.circom文件,生成generate_witness.js,输入good_input.json文件,生成witness.wtns -
设备通过
device.py调用attest.sol的attest方法,来验证证明
核心代码分析
js/manufacturer.js:
该文件是整个项目的核心!
function createResponse(challenge, childSecret) {
let correct_measurements = '0x01020304050607'; // 正确的测量值(根据应用程序可以是任何值,例如内存占用的哈希值等),也就是secret input!
return modSNARK('0x' + sha256([challenge, childSecret, correct_measurements]).toString('hex')); // 生成响应
}
// 注:制造商和设备应该是用的相同的createResponse函数,,论文中作者提到response的生成有三类,上面代码只是一个例子,没有实际意义
function createChallenges(numAtts) {
let challenges = [];
challenges.push('0x' + '1234567890abcdef'); // 初始挑战
const some_secret = '0x' + '4444555555abcdef'; // 一些秘密值
for (let i = 0; i < numAtts; i++) {
challenges.push(modSNARK('0x' + sha256([challenges[0], some_secret, challenges[i]]))); // 生成新的挑战,即cha[i+1] = sha(...)
}
return challenges.slice(1); // 返回挑战数组(去掉初始挑战)
}
circom/attest.sol
function attest(
uint[2] calldata _pA,
uint[2] calldata _pB1,
uint[2] calldata _pB2,
uint[2] calldata _pC, // 电路的中间结果
uint[3] calldata _pubSignals
) public {
...
}
/* 分析函数调用可知:
_pubSignals[0]是DevMekrle_root
_pubSignals[1]是devAddr
_pubSignals[2]是challenge
合约并未指定response,可根据需求自己实现,并作为参数传递
*/
运行环境
node + snarkjs
curl -o- https://raw.githubuserconten t.com/nvm-sh/nvm/v0.39.3/install.sh | bash
source ̃/.bashrc
nvm install v16.20.0
npm install -g snarkjs
sudo apt-get install bc
circom
Installation - Circom 2 Documentation
python
本人运行代码时python环境的requierments.txt:
attrs==24.3.0
bcrypt==4.2.1
certifi==2024.12.14
cffi==1.17.1
charset-normalizer==3.4.1
cryptography==44.0.0
galois==0.3.10
idna==3.10
iniconfig==2.0.0
llvmlite==0.42.0
numba==0.59.1
numpy==1.26.4
packaging==24.2
paramiko==3.5.0
pluggy==1.5.0
poseidon==0.3.1
poseidon-hash==0.1.4
py==1.11.0
pycparser==2.22
PyNaCl==1.5.0
pytest==7.1.3
requests==2.32.3
simplejson==3.19.3
tomli==2.2.1
typing_extensions==4.12.2
urllib3==2.3.0
js
cd ~zk-remote-attestation/js
npm install
node
证明生成流程
电路编译、生成证明、导出合约精简命令如下
命令作用请查看 github.com/iden3/snark… 文档
注:以下使用的good_input从benchmarking/ra40文件夹复制,读者可自行设置
cd circom
circom zRA.circom --r1cs --wasm --sym --c
node zRA_js/generate_witness.js zRA_js/zRA.wasm good_input.json witness.wtns
wget https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_16.ptau -O pot16_final.ptau
snarkjs groth16 setup zRA.r1cs pot16_final.ptau zRA.zkey
snarkjs groth16 prove zRA.zkey witness.wtns proof.json public.json
snarkjs zkey export solidityverifier zRA.zkey verifier_test.sol
ls
# 之后按需将验证合约部署到区块链,这里不赘述
以下为上面命令的具体输入,可供参考
wenmou@LAPTOP-K7MOCBEL:~/code/py311/zk-remote-attestation/circom$ circom zRA.circom --r1cs --wasm --sym --c
warning[P1004]: File "zRA.circom" does not include pragma version. Assuming pragma version (2, 2, 1)
= At the beginning of file "zRA.circom", you should add the directive "pragma circom <Version>", to indicate which compiler version you are using.
warning[P1004]: File "/home/wenmou/code/py311/zk-remote-attestation/circom/tornado-core/circuits/merkleTree.circom" does not include pragma version. Assuming pragma version (2, 2, 1)
= At the beginning of file "/home/wenmou/code/py311/zk-remote-attestation/circom/tornado-core/circuits/merkleTree.circom", you should add the directive "pragma circom <Version>", to indicate which compiler version you are using.
template instances: 144
non-linear constraints: 10104
linear constraints: 11301
public inputs: 3
private inputs: 81
public outputs: 0
wires: 21449
labels: 32102
Written successfully: ./zRA.r1cs
Written successfully: ./zRA.sym
Written successfully: ./zRA_cpp/zRA.cpp and ./zRA_cpp/zRA.dat
Written successfully: ./zRA_cpp/main.cpp, circom.hpp, calcwit.hpp, calcwit.cpp, fr.hpp, fr.cpp, fr.asm and Makefile
Written successfully: ./zRA_js/zRA.wasm
Everything went okay
wenmou@LAPTOP-K7MOCBEL:~/code/py311/zk-remote-attestation/circom$ node zRA_js/generate_witness.js zRA_js/zRA.wasm good_input.json witness.wtns
wenmou@LAPTOP-K7MOCBEL:~/code/py311/zk-remote-attestation/circom$ snarkjs groth16 setup zRA.r1cs pot16_final.ptau zRA.zkey
[INFO] snarkJS: Reading r1cs
[INFO] snarkJS: Reading tauG1
[INFO] snarkJS: Reading tauG2
[INFO] snarkJS: Reading alphatauG1
[INFO] snarkJS: Reading betatauG1
[INFO] snarkJS: Circuit hash:
1a6c4a04 ad8d7bcc 2148e9fd 3ab295f8
411468a7 b2fd32e7 988afcf3 0fa8323c
6fbd74b3 11504e49 81483f5d 9e14a236
19812d56 7eaa9e31 0e06abe3 ee6b4e16
wenmou@LAPTOP-K7MOCBEL:~/code/py311/zk-remote-attestation/circom$ snarkjs groth16 prove zRA.zkey witness.wtns proof.json public.json
wenmou@LAPTOP-K7MOCBEL:~/code/py311/zk-remote-attestation/circom$ snarkjs zkey export solidityverifier zRA.zkey verifier_test.sol
[INFO] snarkJS: EXPORT VERIFICATION KEY STARTED
[INFO] snarkJS: > Detected protocol: groth16
[INFO] snarkJS: EXPORT VERIFICATION KEY FINISHED
wenmou@LAPTOP-K7MOCBEL:~/code/py311/zk-remote-attestation/circom$ ls
attest.sol proof.json tornado-core zRA.circom zRA_cpp
circomlib public.json verifier.sol zRA.r1cs zRA_js
good_input.json sample_proof.json verifier_test.sol zRA.sym
pot16_final.ptau sample_public.json witness.wtns zRA.zkey