Bowe等人2019年论文《Halo: Recursive Proof Composition without a Trusted Setup》。
代码实现参见:
本博文解析主要针对 https://github.com/ebfull/halo
中代码进行解析。
在Halo论文中,使用 G \mathbb{G} G来表示a group of prime order p p p, F p \mathbb{F}_p Fp表示其scalar field,也可简化为用 F \mathbb{F} F来表示scalar field。
Halo论文中的verifier challenge取值范围不是来自于所有的 F x \mathbb{F}^x Fx,而是来自于challenge space I ⊂ F x \mathbb{I}\subset \mathbb{F}^x I⊂Fx,size为 2 λ 2^{\lambda} 2λ。论文中 λ = 128 \lambda=128 λ=128,即相应的Verifier challenge space I \mathbb{I} I的size为 2 128 2^{128} 2128,即Verifier challenge的取值范围应为 ( 0 , 2 128 − 1 ] (0,2^{128}-1] (0,2128−1]。
实际实现时,采用了Abdelrahaman 等人2019年论文《Design of Symmetric-Key Primitives for Advanced Cryptographic Protocols》中的Rescue算法(algebraic hash function for prime fields)来获取Verifier challenges,同时采用Bertoni等人2011年论文《Duplexing the sponge: Single-pass authenticated encryption and other applications》中的duplex sponge来实例化构建Rescue中所需使用sponge函数——Prover messages are “absorbed” and Verifier challenges are “squeezed”。
Rescue的算法实现见https://github.com/ebfull/halo/blob/master/src/rescue.rs
。
new
:pub fn new() -> Self {
let mds_matrix = generate_mds_matrix();
// To use Rescue as a permutation, fix the master key to zero
let key_schedule = generate_key_schedule([F::zero(); RESCUE_M], &mds_matrix);
Rescue {
sponge: SpongeState::Absorbing([None; SPONGE_RATE]),
state: [F::zero(); RESCUE_M],
mds_matrix,
key_schedule,
}
}
absorb
:pub fn absorb(&mut self, val: F) {
match self.sponge {
SpongeState::Absorbing(ref mut input) => {
for entry in input.iter_mut() {
if entry.is_none() {
*entry = Some(val);
return;
}
}
// We've already absorbed as many elements as we can
let _ = rescue_duplex(&mut self.state, input, &self.mds_matrix, &self.key_schedule);
self.sponge = SpongeState::absorb(val);
}
SpongeState::Squeezing(_) => {
// Drop the remaining output elements
self.sponge = SpongeState::absorb(val);
}
}
}
squeeze
:pub fn squeeze(&mut self) -> F {
loop {
match self.sponge {
SpongeState::Absorbing(input) => {
self.sponge = SpongeState::Squeezing(rescue_duplex(
&mut self.state,
&input,
&self.mds_matrix,
&self.key_schedule,
));
}
SpongeState::Squeezing(ref mut output) => {
for entry in output.iter_mut() {
if let Some(e) = entry.take() {
return e;
}
}
// We've already squeezed out all available elements
self.sponge = SpongeState::Absorbing([None; SPONGE_RATE]);
}
}
}
}
1)调用Rescue算法中的squeeze算法,get_challenge
函数中squeeze出来的值大于 2 128 2^{128} 2128,然后get_lower_128()
取其中的低128位,此时值仍然不满足Verifier challenge要求的 ( 0 , 2 128 − 1 ] (0,2^{128}-1] (0,2128−1],方法是将128位的challenge
的最高位置1
,此时范围为 ( 2 127 , 2 129 − 1 ] (2^{127},2^{129}-1] (2127,2129−1]。
其实get_challenge
和get_challenge_scalar
返回的是 u i 2 u_i^2 ui2,是因为在Halo 协议中,Verifier需计算 [ u i 2 ] L [u_i^2]L [ui2]L和 [ u i − 2 ] R [u_i^{-2}]R [ui−2]R,优化依据参见Halo论文6.3节“Other Optimizations”。
fn get_challenge(transcript: &mut Rescue) -> F2 {
let challenge = transcript.squeeze();
let challenge = challenge.get_lower_128();
let challenge = challenge | (1 << 127); //此时范围为$(2^128,2^{129}-1]$
F2::from_u128(challenge)
}
2)将squeeze出来处理后的低128位,来自于Field F1,需要将其转换为F2 Field内的scalar值:
pub fn get_challenge_scalar(challenge: F1) -> F2 {
let challenge = challenge.get_lower_128();
let mut acc = F2::one();
acc = acc + acc;
acc = acc + F2::one();
for i in 1..64 {
let should_negate = (challenge >> (i * 2)) & 1 == 1;
let should_endo = (challenge >> (i * 2 + 1)) & 1 == 1;
acc = acc + &acc;
if should_negate {
acc = acc - &F2::one();
} else {
acc = acc + &F2::one();
}
if should_endo {
acc = acc * &F2::BETA;
}
}
acc
}
3)注意论文中 u 1 , u 2 , ⋯ , u k u_1,u_2,\cdots,u_k u1,u2,⋯,uk的取值范围为 ( 0 , 2 128 − 1 ] (0,2^{128}-1] (0,2128−1],而get_challenge
返回的值范围在 ( 2 1 27 , 2 129 − 1 ] (2^127,2^{129}-1] (2127,2129−1],调用get_challenge_scalar
返回的scalar值在(0,2^{129}-1],需要保证该scalar值的sqrt值存在,最后sqrt的结果即为相应的Verifier challenge u i u_i ui。
let (challenge, challenge_sq, challenge_sq_packed) = loop {
let mut transcript = transcript.clone();
transcript.absorb(forkvalue);
let challenge_sq_packed = get_challenge::<_, C::Scalar>(&mut transcript);
let challenge_sq: C::Scalar = get_challenge_scalar(challenge_sq_packed);
match challenge_sq.sqrt().to_option() {
Some(challenge) => {
break (challenge, challenge_sq, challenge_sq_packed);
}
None => {
forkvalue = forkvalue + &C::Base::one();
forkvalue_u8 += 1;
}
}
};
完整协议参见Halo论文Figure 1,以及博客 Halo: Recursive Proof Composition without a Trusted Setup 学习笔记 第4节内容。