Zengo-X 的 MPC GG20 算法底层实现机制
一.MPC 和 GG20 概述
MPC(Multi-Party Computation,多方安全计算)是密码学中的一个重要分支,用于在不需要可信第三方的情况下保护计算中的隐私。GG20 协议是一个基于 MPC 的门限签名协议,主要用于生成和管理加密密钥。我们都知道,MPC 的底层算法还是门限签名,门限签名是一种允许一组参与者(例如,n个参与者中的任意t个)联合签署一条消息的协议。这种签名方式无需所有参与者都参与,只需达到门限 t 即可。签名生成的过程中,密钥从未在一个地方集中保存,分布在多个参与者之间,从而提高了安全性。
1.GG20 的密钥生成
- 分布式密钥生成:所有参与者通过 MPC 协议共同生成一对公私钥。每个参与者持有私钥的一部分(称为私钥份额),公钥是所有参与者共同计算得到的。
- 分享私钥份额:每个参与者生成一个秘密值,并将其通过安全多方计算协议分享给其他参与者。这样,每个参与者最终拥有一个私钥份额。
2.签名生成(Signature Generation)
- 消息预处理:在签名消息之前,参与者需要对消息进行预处理。这个过程通常包括消息的哈希计算和一些参数的生成。
- 部分签名计算:每个参与者使用自己的私钥份额对消息进行部分签名。这一步通过 MPC 协议进行,保证私钥份额不会被泄露。
- 部分签名聚合:所有参与者的部分签名通过 MPC 协议进行聚合,生成完整的签名。
3.安全保障
- 秘密共享:利用秘密共享方案(如Shamir秘密共享)确保私钥份额的安全性。私钥份额在传输过程中经过加密,保证传输安全。
- 零知识证明:使用零知识证明技术,确保参与者在签名计算过程中未泄露私钥份额。
二. GG20 算法的 Keygen 流程和代码详解
1. Keygen 流程
GG20 协议中的密钥生成(Keygen)过程分为多个轮次(Rounds),每个轮次都有特定的任务和步骤。下面是每个轮次的详细说明:
1.1.Round 0: 初始化
- 参数生成:每个参与者生成一些本地参数,例如,随机数、私钥份额等。
- 信息广播:每个参与者向其他参与者广播一些公共信息,这些信息将在后续的轮次中使用。
1.2.Round 1: 承诺阶段
- 生成承诺:每个参与者生成一个与其私钥份额相关的承诺(Commitment)。这个承诺可以通过Pedersen承诺方案来实现,确保承诺是绑定和隐藏的。
- 广播承诺:每个参与者将生成的承诺广播给所有其他参与者。
1.3.Round 2: 验证和共享秘密
- 验证承诺:每个参与者验证其他参与者的承诺,确保其有效性和一致性。
- 生成和共享秘密:每个参与者生成一个秘密值,并将其通过秘密共享方案(如Shamir秘密共享)分享给其他参与者。
1.4.Round 3: 验证秘密
- 接收和验证秘密:每个参与者接收其他参与者分享的秘密份额,并验证这些份额的有效性。
- 计算中间值:每个参与者使用接收到的秘密份额和本地参数计算一些中间值,这些值将在最终的密钥生成中使用。
1.5.Round 4: 生成公私钥
- 计算私钥份额:每个参与者结合接收到的所有秘密份额和中间值,计算出自己的私钥份额。
- 生成公钥:所有参与者共同计算生成公钥。这个公钥是所有参与者的公钥,用于验证签名。
1.6.总结
在整个密钥生成过程中,各个轮次的具体任务和步骤如下:
- Round 0:生成本地参数并广播公共信息。
- Round 1:生成和广播承诺,确保每个参与者的私钥份额是隐藏和绑定的。
- Round 2:验证承诺,并生成和共享秘密份额。
- Round 3:接收和验证秘密份额,计算中间值。
- Round 4:计算私钥份额和生成公钥。
这些步骤通过安全多方计算(MPC)协议进行,确保私钥份额在生成和传输过程中始终保持私密,且没有任何单个参与者能够独立重构完整的私钥。这种机制极大地提高了系统的安全性,适用于需要高安全性的分布式环境,如区块链和多方认证系统。
2. Keygen 代码
- Round 0:
pub struct Round0 {
pub party_i: u16,
pub t: u16,
pub n: u16,
}
impl Round0 {
pub fn proceed<O>(self, mut output: O) -> Result<Round1>
where
O: Push<Msg<gg_2020::party_i::KeyGenBroadcastMessage1>>,
{
let party_keys = Keys::create(self.party_i as usize);
let (bc1, decom1) =
party_keys.phase1_broadcast_phase3_proof_of_correct_key_proof_of_correct_h1h2();
output.push(Msg {
sender: self.party_i,
receiver: None,
body: bc1.clone(),
});
Ok(Round1 {
keys: party_keys,
bc1,
decom1,
party_i: self.party_i,
t: self.t,
n: self.n,
})
}
pub fn is_expensive(&self) -> bool {
true
}
}
-
phase1_broadcast_phase3_proof_of_correct_key_proof_of_correct_h1h2:生成正确的 key 的 proof 和 h1、h2 proof; Proof of correct key 是一种零知识证明,用于确保生成的密钥片段和相关计算的正确性和一致性。这是为了确保每个参与者在生成密钥片段时没有作弊或出现错误,从而提高整个协议的安全性和可靠性;proof of h1 和 h2 是零知识证明的一部分,用于证明某些计算的正确性和一致性。这些证明有助于确保协议中的安全性和诚信。
-
Round 1:
pub struct Round1 {
keys: Keys,
bc1: KeyGenBroadcastMessage1,
decom1: KeyGenDecommitMessage1,
party_i: u16,
t: u16,
n: u16,
}
impl Round1 {
pub fn proceed<O>(
self,
input: BroadcastMsgs<KeyGenBroadcastMessage1>,
mut output: O,
) -> Result<Round2>
where
O: Push<Msg<gg_2020::party_i::KeyGenDecommitMessage1>>,
{
output.push(Msg {
sender: self.party_i,
receiver: None,
body: self.decom1.clone(),
});
Ok(Round2 {
keys: self.keys,
received_comm: input.into_vec_including_me(self.bc1),
decom: self.decom1,
party_i: self.party_i,
t: self.t,
n: self.n,
})
}
pub fn is_expensive(&self) -> bool {
false
}
pub fn expects_messages(i: u16, n: u16) -> Store<BroadcastMsgs<KeyGenBroadcastMessage1>> {
containers::BroadcastMsgsStore::new(i, n)
}
}
-
生成 Keygen 广播承诺
-
Round 2:
pub struct Round2 {
keys: gg_2020::party_i::Keys,
received_comm: Vec<KeyGenBroadcastMessage1>,
decom: KeyGenDecommitMessage1,
party_i: u16,
t: u16,
n: u16,
}
impl Round2 {
pub fn proceed<O>(
self,
input: BroadcastMsgs<KeyGenDecommitMessage1>,
mut output: O,
) -> Result<Round3>
where
O: Push<Msg<(VerifiableSS<Secp256k1>, Scalar<Secp256k1>)>>,
{
let params = gg_2020::party_i::Parameters {
threshold: self.t,
share_count: self.n,
};
let received_decom = input.into_vec_including_me(self.decom);
let vss_result = self
.keys
.phase1_verify_com_phase3_verify_correct_key_verify_dlog_phase2_distribute(
¶ms,
&received_decom,
&self.received_comm,
)
.map_err(ProceedError::Round2VerifyCommitments)?;
for (i, share) in vss_result.1.iter().enumerate() {
if i + 1 == usize::from(self.party_i) {
continue;
}
output.push(Msg {
sender: self.party_i,
receiver: Some(i as u16 + 1),
body: (vss_result.0.clone(), share.clone()),
})
}
Ok(Round3 {
keys: self.keys,
y_vec: received_decom.into_iter().map(|d| d.y_i).collect(),
bc_vec: self.received_comm,
own_vss: vss_result.0.clone(),
own_share: vss_result.1[usize::from(self.party_i - 1)].clone(),
party_i: self.party_i,
t: self.t,
n: self.n,
})
}
pub fn is_expensive(&self) -> bool {
true
}
pub fn expects_messages(i: u16, n: u16) -> Store<BroadcastMsgs<KeyGenDecommitMessage1>> {
containers::BroadcastMsgsStore::new(i, n)
}
}
-
验证承诺,并生成和共享秘密份额。
-
Round 3:
pub struct Round3 {
keys: gg_2020::party_i::Keys,
y_vec: Vec<Point<Secp256k1>>,
bc_vec: Vec<gg_2020::party_i::KeyGenBroadcastMessage1>,
own_vss: VerifiableSS<Secp256k1>,
own_share: Scalar<Secp256k1>,
party_i: u16,
t: u16,
n: u16,
}
impl Round3 {
pub fn proceed<O>(
self,
input: P2PMsgs<(VerifiableSS<Secp256k1>, Scalar<Secp256k1>)>,
mut output: O,
) -> Result<Round4>
where
O: Push<Msg<DLogProof<Secp256k1, Sha256>>>,
{
let params = gg_2020::party_i::Parameters {
threshold: self.t,
share_count: self.n,
};
let (vss_schemes, party_shares): (Vec<_>, Vec<_>) = input
.into_vec_including_me((self.own_vss, self.own_share))
.into_iter()
.unzip();
let (shared_keys, dlog_proof) = self
.keys
.phase2_verify_vss_construct_keypair_phase3_pok_dlog(
¶ms,
&self.y_vec,
&party_shares,
&vss_schemes,
self.party_i.into(),
)
.map_err(ProceedError::Round3VerifyVssConstruct)?;
output.push(Msg {
sender: self.party_i,
receiver: None,
body: dlog_proof.clone(),
});
Ok(Round4 {
keys: self.keys.clone(),
y_vec: self.y_vec.clone(),
bc_vec: self.bc_vec,
shared_keys,
own_dlog_proof: dlog_proof,
vss_vec: vss_schemes,
party_i: self.party_i,
t: self.t,
n: self.n,
})
}
pub fn is_expensive(&self) -> bool {
true
}
pub fn expects_messages(
i: u16,
n: u16,
) -> Store<P2PMsgs<(VerifiableSS<Secp256k1>, Scalar<Secp256k1>)>> {
containers::P2PMsgsStore::new(i, n)
}
}
-
接收和验证秘密份额,计算中间值。
-
Round 4:
pub struct Round4 {
keys: gg_2020::party_i::Keys,
y_vec: Vec<Point<Secp256k1>>,
bc_vec: Vec<gg_2020::party_i::KeyGenBroadcastMessage1>,
shared_keys: gg_2020::party_i::SharedKeys,
own_dlog_proof: DLogProof<Secp256k1, Sha256>,
vss_vec: Vec<VerifiableSS<Secp256k1>>,
party_i: u16,
t: u16,
n: u16,
}
impl Round4 {
pub fn proceed(
self,
input: BroadcastMsgs<DLogProof<Secp256k1, Sha256>>,
) -> Result<LocalKey<Secp256k1>> {
let params = gg_2020::party_i::Parameters {
threshold: self.t,
share_count: self.n,
};
let dlog_proofs = input.into_vec_including_me(self.own_dlog_proof.clone());
Keys::verify_dlog_proofs_check_against_vss(
¶ms,
&dlog_proofs,
&self.y_vec,
&self.vss_vec,
)
.map_err(ProceedError::Round4VerifyDLogProof)?;
let pk_vec = (0..params.share_count as usize)
.map(|i| dlog_proofs[i].pk.clone())
.collect::<Vec<Point<Secp256k1>>>();
let paillier_key_vec = (0..params.share_count)
.map(|i| self.bc_vec[i as usize].e.clone())
.collect::<Vec<EncryptionKey>>();
let h1_h2_n_tilde_vec = self
.bc_vec
.iter()
.map(|bc1| bc1.dlog_statement.clone())
.collect::<Vec<DLogStatement>>();
let (head, tail) = self.y_vec.split_at(1);
let y_sum = tail.iter().fold(head[0].clone(), |acc, x| acc + x);
let local_key = LocalKey {
paillier_dk: self.keys.dk,
pk_vec,
keys_linear: self.shared_keys.clone(),
paillier_key_vec,
y_sum_s: y_sum,
h1_h2_n_tilde_vec,
vss_scheme: self.vss_vec[usize::from(self.party_i - 1)].clone(),
i: self.party_i,
t: self.t,
n: self.n,
};
Ok(local_key)
}
pub fn is_expensive(&self) -> bool {
true
}
pub fn expects_messages(i: u16, n: u16) -> Store<BroadcastMsgs<DLogProof<Secp256k1, Sha256>>> {
containers::BroadcastMsgsStore::new(i, n)
}
}
- 计算私钥份额和生成公钥
三. GG20 算法的 Sign 流程和代码详解
在GG20协议的签名生成(Sign)过程中,每个轮次(Rounds)都有特定的任务和步骤
1. Sign 流程
1.1.Round 0: 初始化
参数初始化:
- 每个参与者加载他们在密钥生成阶段生成的私钥片段和公钥。
-
确定待签名的消息 m。 生成随机数:
-
每个参与者生成一个随机数 $$k_i$$
计算部分承诺:
- 计算部分承诺 $$R_i$$: $$ R_i = g^{k_i} \mod p$$
1.2.Round 1: 广播承诺
- 广播部分承诺:每个参与者将其部分承诺 $$R_i$$ 广播给所有其他参与者。
- 收集承诺:每个参与者收集所有其他参与者的承诺 $$R_i$$
1.3.Round 2: 生成聚合承诺
- 计算聚合承诺: 每个参与者计算聚合承诺 R:
$$ R = \prod_{i=1}^{n} R_i$$
- 广播聚合承诺:将聚合承诺 R 广播给所有参与者。
1.4.Round 3: 生成挑战
- 生成挑战:每个参与者使用哈希函数生成挑战 c,通常使用聚合承诺 R 和消息 m:
$$c = H(R, m)$$
1.5.Round 4: 计算部分签名
- 计算部分签名:每个参与者计算其部分签名 $$s_i$$:
$$s_i = k_i + c \cdot x_i \mod q$$
- 广播部分签名:每个参与者将其部分签名 $$s_i$$ 广播给所有其他参与者。
1.6.Round 5: 收集和验证部分签名
- 收集部分签名:每个参与者收集所有其他参与者的部分签名 $$s_i$$
- 验证部分签名:每个参与者验证其他参与者的部分签名 $$s_i$$ 是否正确。
1.7.Round 6: 计算和验证最终签名
计算最终签名:使用收集到的部分签名计算最终签名 s:
$$s = \sum_{i=1}^{n} s_i \mod q$$
验证最终签名:验证最终签名是否正确:
$$g^s \overset{?}{=} R \cdot Y^c \mod p$$
1.8. 总结
- Round 0:初始化参与者参数,生成随机数和部分承诺。
- Round 1:广播部分承诺。
- Round 2:计算并广播聚合承诺。
- Round 3:生成挑战。
- Round 4:计算并广播部分签名。
- Round 5:收集和验证部分签名。
- Round 6:计算和验证最终签名。
2.Sign 代码流程
- Round 0:初始化参与者参数,生成随机数和部分承诺。
pub struct Round0 {
/// Index of this party
///
/// Must be in range `[0; n)` where `n` is number of parties involved in signing.
pub i: u16,
/// List of parties' indexes from keygen protocol
///
/// I.e. `s_l[i]` must be an index of party `i` that was used by this party in keygen protocol.
// s_l.len()` equals to `n` (number of parties involved in signing)
pub s_l: Vec<u16>,
/// Party local secret share
pub local_key: LocalKey<Secp256k1>,
}
impl Round0 {
pub fn proceed<O>(self, mut output: O) -> Result<Round1>
where
O: Push<Msg<(MessageA, SignBroadcastPhase1)>>,
{
let sign_keys = SignKeys::create(
&self.local_key.keys_linear.x_i,
&self.local_key.vss_scheme.clone(),
usize::from(self.s_l[usize::from(self.i - 1)]) - 1,
&self
.s_l
.iter()
.map(|&i| usize::from(i) - 1)
.collect::<Vec<_>>(),
);
let (bc1, decom1) = sign_keys.phase1_broadcast();
let party_ek = self.local_key.paillier_key_vec[usize::from(self.local_key.i - 1)].clone();
let m_a = MessageA::a(&sign_keys.k_i, &party_ek, &self.local_key.h1_h2_n_tilde_vec);
output.push(Msg {
sender: self.i,
receiver: None,
body: (m_a.0.clone(), bc1.clone()),
});
let round1 = Round1 {
i: self.i,
s_l: self.s_l.clone(),
local_key: self.local_key,
m_a,
sign_keys,
phase1_com: bc1,
phase1_decom: decom1,
};
Ok(round1)
}
pub fn is_expensive(&self) -> bool {
true
}
}
- Round 1:广播部分承诺。
pub struct Round1 {
i: u16,
s_l: Vec<u16>,
local_key: LocalKey<Secp256k1>,
m_a: (MessageA, BigInt),
sign_keys: SignKeys,
phase1_com: SignBroadcastPhase1,
phase1_decom: SignDecommitPhase1,
}
impl Round1 {
pub fn proceed<O>(
self,
input: BroadcastMsgs<(MessageA, SignBroadcastPhase1)>,
mut output: O,
) -> Result<Round2>
where
O: Push<Msg<(GammaI, WI)>>,
{
let (m_a_vec, bc_vec): (Vec<_>, Vec<_>) = input
.into_vec_including_me((self.m_a.0.clone(), self.phase1_com.clone()))
.into_iter()
.unzip();
let mut m_b_gamma_vec = Vec::new();
let mut beta_vec = Vec::new();
let mut m_b_w_vec = Vec::new();
let mut ni_vec = Vec::new();
let ttag = self.s_l.len();
let l_s: Vec<_> = self
.s_l
.iter()
.cloned()
.map(|i| usize::from(i) - 1)
.collect();
let i = usize::from(self.i - 1);
for j in 0..ttag - 1 {
let ind = if j < i { j } else { j + 1 };
let (m_b_gamma, beta_gamma, _beta_randomness, _beta_tag) = MessageB::b(
&self.sign_keys.gamma_i,
&self.local_key.paillier_key_vec[l_s[ind]],
m_a_vec[ind].clone(),
&self.local_key.h1_h2_n_tilde_vec,
)
.map_err(|e| {
Error::Round1(ErrorType {
error_type: e.to_string(),
bad_actors: vec![],
})
})?;
let (m_b_w, beta_wi, _, _) = MessageB::b(
&self.sign_keys.w_i,
&self.local_key.paillier_key_vec[l_s[ind]],
m_a_vec[ind].clone(),
&self.local_key.h1_h2_n_tilde_vec,
)
.map_err(|e| {
Error::Round1(ErrorType {
error_type: e.to_string(),
bad_actors: vec![],
})
})?;
m_b_gamma_vec.push(m_b_gamma);
beta_vec.push(beta_gamma);
m_b_w_vec.push(m_b_w);
ni_vec.push(beta_wi);
}
let party_indices = (1..=self.s_l.len())
.map(|j| u16::try_from(j).unwrap())
.filter(|&j| j != self.i);
for ((j, gamma_i), w_i) in party_indices.zip(m_b_gamma_vec).zip(m_b_w_vec) {
output.push(Msg {
sender: self.i,
receiver: Some(j),
body: (GammaI(gamma_i.clone()), WI(w_i.clone())),
});
}
Ok(Round2 {
i: self.i,
s_l: self.s_l,
local_key: self.local_key,
sign_keys: self.sign_keys,
m_a: self.m_a,
beta_vec,
ni_vec,
bc_vec,
m_a_vec,
phase1_decom: self.phase1_decom,
})
}
pub fn expects_messages(
i: u16,
n: u16,
) -> Store<BroadcastMsgs<(MessageA, SignBroadcastPhase1)>> {
containers::BroadcastMsgsStore::new(i, n)
}
pub fn is_expensive(&self) -> bool {
true
}
}
- Round 2:计算并广播聚合承诺。
pub struct Round2 {
i: u16,
s_l: Vec<u16>,
local_key: LocalKey<Secp256k1>,
sign_keys: SignKeys,
m_a: (MessageA, BigInt),
beta_vec: Vec<Scalar<Secp256k1>>,
ni_vec: Vec<Scalar<Secp256k1>>,
bc_vec: Vec<SignBroadcastPhase1>,
m_a_vec: Vec<MessageA>,
phase1_decom: SignDecommitPhase1,
}
impl Round2 {
pub fn proceed<O>(self, input_p2p: P2PMsgs<(GammaI, WI)>, mut output: O) -> Result<Round3>
where
O: Push<Msg<(DeltaI, TI, TIProof)>>, // TODO: unify TI and TIProof
{
let (m_b_gamma_s, m_b_w_s): (Vec<_>, Vec<_>) = input_p2p
.into_vec()
.into_iter()
.map(|(gamma_i, w_i)| (gamma_i.0, w_i.0))
.unzip();
let mut alpha_vec = Vec::new();
let mut miu_vec = Vec::new();
let ttag = self.s_l.len();
let index = usize::from(self.i) - 1;
let l_s: Vec<_> = self
.s_l
.iter()
.cloned()
.map(|i| usize::from(i) - 1)
.collect();
let g_w_vec = SignKeys::g_w_vec(
&self.local_key.pk_vec[..],
&l_s[..],
&self.local_key.vss_scheme,
);
for j in 0..ttag - 1 {
let ind = if j < index { j } else { j + 1 };
let m_b = m_b_gamma_s[j].clone();
let alpha_ij_gamma = m_b
.verify_proofs_get_alpha(&self.local_key.paillier_dk, &self.sign_keys.k_i)
.map_err(|e| {
Error::Round3(ErrorType {
error_type: e.to_string(),
bad_actors: vec![],
})
})?;
let m_b = m_b_w_s[j].clone();
let alpha_ij_wi = m_b
.verify_proofs_get_alpha(&self.local_key.paillier_dk, &self.sign_keys.k_i)
.map_err(|e| {
Error::Round3(ErrorType {
error_type: e.to_string(),
bad_actors: vec![],
})
})?;
assert_eq!(m_b.b_proof.pk, g_w_vec[ind]); //TODO: return error
alpha_vec.push(alpha_ij_gamma.0);
miu_vec.push(alpha_ij_wi.0);
}
let delta_i = self.sign_keys.phase2_delta_i(&alpha_vec, &self.beta_vec);
let sigma_i = self.sign_keys.phase2_sigma_i(&miu_vec, &self.ni_vec);
let (t_i, l_i, t_i_proof) = SignKeys::phase3_compute_t_i(&sigma_i);
output.push(Msg {
sender: self.i,
receiver: None,
body: (
DeltaI(delta_i.clone()),
TI(t_i.clone()),
TIProof(t_i_proof.clone()),
),
});
Ok(Round3 {
i: self.i,
s_l: self.s_l,
local_key: self.local_key,
sign_keys: self.sign_keys,
m_a: self.m_a,
mb_gamma_s: m_b_gamma_s,
bc_vec: self.bc_vec,
m_a_vec: self.m_a_vec,
delta_i,
t_i,
l_i,
sigma_i,
t_i_proof,
phase1_decom: self.phase1_decom,
})
}
pub fn expects_messages(i: u16, n: u16) -> Store<P2PMsgs<(GammaI, WI)>> {
containers::P2PMsgsStore::new(i, n)
}
pub fn is_expensive(&self) -> bool {
true
}
}
- Round 3:生成挑战。
pub struct Round3 {
i: u16,
s_l: Vec<u16>,
local_key: LocalKey<Secp256k1>,
sign_keys: SignKeys,
m_a: (MessageA, BigInt),
mb_gamma_s: Vec<MessageB>,
bc_vec: Vec<SignBroadcastPhase1>,
m_a_vec: Vec<MessageA>,
delta_i: Scalar<Secp256k1>,
t_i: Point<Secp256k1>,
l_i: Scalar<Secp256k1>,
sigma_i: Scalar<Secp256k1>,
t_i_proof: PedersenProof<Secp256k1, Sha256>,
phase1_decom: SignDecommitPhase1,
}
impl Round3 {
pub fn proceed<O>(
self,
input: BroadcastMsgs<(DeltaI, TI, TIProof)>,
mut output: O,
) -> Result<Round4>
where
O: Push<Msg<SignDecommitPhase1>>,
{
let (delta_vec, t_vec, t_proof_vec) = input
.into_vec_including_me((
DeltaI(self.delta_i),
TI(self.t_i.clone()),
TIProof(self.t_i_proof),
))
.into_iter()
.map(|(delta_i, t_i, t_i_proof)| (delta_i.0, t_i.0, t_i_proof.0))
.unzip3();
for i in 0..t_vec.len() {
assert_eq!(t_vec[i], t_proof_vec[i].com);
}
let delta_inv = SignKeys::phase3_reconstruct_delta(&delta_vec);
let ttag = self.s_l.len();
for proof in t_proof_vec.iter().take(ttag) {
PedersenProof::verify(proof).map_err(|e| {
Error::Round3(ErrorType {
error_type: e.to_string(),
bad_actors: vec![],
})
})?;
}
output.push(Msg {
sender: self.i,
receiver: None,
body: self.phase1_decom.clone(),
});
Ok(Round4 {
i: self.i,
s_l: self.s_l,
local_key: self.local_key,
sign_keys: self.sign_keys,
m_a: self.m_a,
mb_gamma_s: self.mb_gamma_s,
bc_vec: self.bc_vec,
m_a_vec: self.m_a_vec,
t_i: self.t_i,
l_i: self.l_i,
sigma_i: self.sigma_i,
phase1_decom: self.phase1_decom,
delta_inv,
t_vec,
})
}
pub fn expects_messages(i: u16, n: u16) -> Store<BroadcastMsgs<(DeltaI, TI, TIProof)>> {
containers::BroadcastMsgsStore::new(i, n)
}
pub fn is_expensive(&self) -> bool {
true
}
}
- Round 4:计算并广播部分签名。
impl Round4 {
pub fn proceed<O>(
self,
decommit_round1: BroadcastMsgs<SignDecommitPhase1>,
mut output: O,
) -> Result<Round5>
where
O: Push<Msg<(RDash, Vec<PDLwSlackProof>)>>,
{
let decom_vec: Vec<_> = decommit_round1.into_vec_including_me(self.phase1_decom.clone());
let ttag = self.s_l.len();
let b_proof_vec: Vec<_> = (0..ttag - 1).map(|i| &self.mb_gamma_s[i].b_proof).collect();
let R = SignKeys::phase4(
&self.delta_inv,
&b_proof_vec[..],
decom_vec,
&self.bc_vec,
usize::from(self.i - 1),
)
.map_err(Error::Round5)?;
let R_dash = &R * &self.sign_keys.k_i;
// each party sends first message to all other parties
let mut phase5_proofs_vec = Vec::new();
let l_s: Vec<_> = self
.s_l
.iter()
.cloned()
.map(|i| usize::from(i) - 1)
.collect();
let index = usize::from(self.i - 1);
for j in 0..ttag - 1 {
let ind = if j < index { j } else { j + 1 };
let proof = LocalSignature::phase5_proof_pdl(
&R_dash,
&R,
&self.m_a.0.c,
&self.local_key.paillier_key_vec[l_s[index]],
&self.sign_keys.k_i,
&self.m_a.1,
&self.local_key.h1_h2_n_tilde_vec[l_s[ind]],
);
phase5_proofs_vec.push(proof);
}
output.push(Msg {
sender: self.i,
receiver: None,
body: (RDash(R_dash.clone()), phase5_proofs_vec.clone()),
});
Ok(Round5 {
i: self.i,
s_l: self.s_l,
local_key: self.local_key,
sign_keys: self.sign_keys,
t_vec: self.t_vec,
m_a_vec: self.m_a_vec,
t_i: self.t_i,
l_i: self.l_i,
sigma_i: self.sigma_i,
R,
R_dash,
phase5_proofs_vec,
})
}
pub fn expects_messages(i: u16, n: u16) -> Store<BroadcastMsgs<SignDecommitPhase1>> {
containers::BroadcastMsgsStore::new(i, n)
}
pub fn is_expensive(&self) -> bool {
true
}
}
- Round 5:收集和验证部分签名。
pub struct Round5 {
i: u16,
s_l: Vec<u16>,
local_key: LocalKey<Secp256k1>,
sign_keys: SignKeys,
t_vec: Vec<Point<Secp256k1>>,
m_a_vec: Vec<MessageA>,
t_i: Point<Secp256k1>,
l_i: Scalar<Secp256k1>,
sigma_i: Scalar<Secp256k1>,
R: Point<Secp256k1>,
R_dash: Point<Secp256k1>,
phase5_proofs_vec: Vec<PDLwSlackProof>,
}
impl Round5 {
pub fn proceed<O>(
self,
input: BroadcastMsgs<(RDash, Vec<PDLwSlackProof>)>,
mut output: O,
) -> Result<Round6>
where
O: Push<Msg<(SI, HEGProof)>>,
{
let (r_dash_vec, pdl_proof_mat_inc_me): (Vec<_>, Vec<_>) = input
.into_vec_including_me((RDash(self.R_dash), self.phase5_proofs_vec))
.into_iter()
.map(|(r_dash, pdl_proof)| (r_dash.0, pdl_proof))
.unzip();
let l_s: Vec<_> = self
.s_l
.iter()
.cloned()
.map(|i| usize::from(i) - 1)
.collect();
let ttag = self.s_l.len();
for i in 0..ttag {
LocalSignature::phase5_verify_pdl(
&pdl_proof_mat_inc_me[i],
&r_dash_vec[i],
&self.R,
&self.m_a_vec[i].c,
&self.local_key.paillier_key_vec[l_s[i]],
&self.local_key.h1_h2_n_tilde_vec,
&l_s,
i,
)
.map_err(Error::Round5)?;
}
LocalSignature::phase5_check_R_dash_sum(&r_dash_vec).map_err(|e| {
Error::Round5(ErrorType {
error_type: e.to_string(),
bad_actors: vec![],
})
})?;
let (S_i, homo_elgamal_proof) = LocalSignature::phase6_compute_S_i_and_proof_of_consistency(
&self.R,
&self.t_i,
&self.sigma_i,
&self.l_i,
);
output.push(Msg {
sender: self.i,
receiver: None,
body: (SI(S_i.clone()), HEGProof(homo_elgamal_proof.clone())),
});
Ok(Round6 {
S_i,
homo_elgamal_proof,
s_l: self.s_l,
protocol_output: CompletedOfflineStage {
i: self.i,
local_key: self.local_key,
sign_keys: self.sign_keys,
t_vec: self.t_vec,
R: self.R,
sigma_i: self.sigma_i,
},
})
}
pub fn expects_messages(i: u16, n: u16) -> Store<BroadcastMsgs<(RDash, Vec<PDLwSlackProof>)>> {
containers::BroadcastMsgsStore::new(i, n)
}
pub fn is_expensive(&self) -> bool {
true
}
}
- Round 6:计算和验证最终签名。
pub struct Round6 {
S_i: Point<Secp256k1>,
homo_elgamal_proof: HomoELGamalProof<Secp256k1, Sha256>,
s_l: Vec<u16>,
/// Round 6 guards protocol output until final checks are taken the place
protocol_output: CompletedOfflineStage,
}
impl Round6 {
pub fn proceed(
self,
input: BroadcastMsgs<(SI, HEGProof)>,
) -> Result<CompletedOfflineStage, Error> {
let (S_i_vec, hegp_vec): (Vec<_>, Vec<_>) = input
.into_vec_including_me((SI(self.S_i), HEGProof(self.homo_elgamal_proof)))
.into_iter()
.map(|(s_i, hegp_i)| (s_i.0, hegp_i.0))
.unzip();
let R_vec: Vec<_> = iter::repeat(self.protocol_output.R.clone())
.take(self.s_l.len())
.collect();
LocalSignature::phase6_verify_proof(
&S_i_vec,
&hegp_vec,
&R_vec,
&self.protocol_output.t_vec,
)
.map_err(Error::Round6VerifyProof)?;
LocalSignature::phase6_check_S_i_sum(&self.protocol_output.local_key.y_sum_s, &S_i_vec)
.map_err(Error::Round6CheckSig)?;
Ok(self.protocol_output)
}
pub fn expects_messages(i: u16, n: u16) -> Store<BroadcastMsgs<(SI, HEGProof)>> {
containers::BroadcastMsgsStore::new(i, n)
}
pub fn is_expensive(&self) -> bool {
true
}
}
四.附录: Pedersen 承诺方案
Pedersen承诺方案是一种加密承诺方案,具有绑定性(binding)和隐藏性(hiding)两个特性。这使其在密码学和区块链应用中非常有用,特别是在需要保证数据隐私和完整性的场景中。以下是Pedersen承诺方案的详细说明:
1.基本原理
Pedersen承诺方案基于离散对数问题的困难性,其主要步骤如下:
- 选择公有参数:
- G:一个大素数,通常是一个安全素数。
- g:一个生成元,生成一个素数阶为 q 的子群。
- h:另一个群元素,其中 h 不等于 g,且 h 的离散对数未知。
这些参数可以由信任方生成,也可以在标准中固定。
2.承诺生成
假设有一个值 m 和一个随机数 r,承诺 m 的步骤如下:
- 选择随机数 r:r 是在有限域 Zq 上的一个随机值。
- 计算承诺值 C:$$C=g^m⋅h^r mod G$$
这里,m 是你要承诺的值,r 是用于保证隐藏性的随机数。
3.验证承诺
要验证承诺,接受者需要知道承诺值 C、原始值 m 和随机数 r。验证步骤如下:
3.1.计算承诺值
- 重新计算$$C^′=g^m⋅h^rmod G$$
3.2.比较
- 检查 C′ 是否等于 C。如果相等,则承诺验证通过。
4.特性
- 绑定性(Binding):一旦承诺值 C 确定,承诺者无法在不改变 r 的情况下改变 m。这是因为离散对数问题的困难性,找到另一个 m′ 使得 $$ g^{m'} \cdot h^r = g^m \cdot h^r$$ 在计算上是不可行的。
- 隐藏性(Hiding):在不知道随机数 r 的情况下,无法从承诺值 C 推导出原始值 m。这是因为随机数 r 的存在,使得 C 看起来像是随机值。
5.应用
Pedersen承诺方案在多个领域有广泛应用,包括但不限于:
- 区块链和加密货币:用于隐私保护的交易和智能合约。
- 零知识证明:在零知识证明协议中,作为基础构建模块,用于隐藏证明者的私有信息。
- 多方计算:在多方计算协议中,确保各方共享的秘密值在计算过程中保持私密。
Pedersen 承诺方案通过其绑定性和隐藏性,提供了一种强大的工具,用于在各种密码学协议中保证数据的隐私和完整性。其基于离散对数问题的困难性,使其在当前已知的计算能力下具有高度的安全性。