1. 引言
Rosario Gennaro 和 Steven Goldfeder 2018年论文《Fast Multiparty Threshold ECDSA with Fast Trustless Setup》(也称其为GG18论文),发表于ACM CCS 2018。
开源代码实现见:
- https://github.com/bnb-chain/tss-lib(Go)
- 审计报告见:audit-binance-tss-lib-final-20191018.pdf
- 在ECDSA 2.0版本中新增了两个字段PaillierSK.P和PaillierSK.Q,用于生成Paillier密钥证明,2.0之前版本生成的密钥值需要重新生成(重新共享)密钥值,以更新参数并填写必要的字段。
门限签名方案允许 n n n 个参与者分布式地完成签名,使得任何大小为 t + 1 t+1 t+1 的子集可以生成签名,而大小为 t t t 或更少的子集无法生成签名。尽管已有针对ECDSA签名方案的门限签名方案,但GG18论文中:
- 提出了首个支持任意
t
≤
n
t \leq n
t≤n 的多方签名协议,并实现了高效、无中心节点的密钥生成。
- 该协议相较于现有方案更快,同时显著减少了通信复杂度。
- 证明了该方案在存在恶意多数对手的情况下是安全的,并通过实现验证了该方案的效率和实际可部署性。
1.1 从乘法到加法的份额转换协议
假设有两个参与方 Alice 和 Bob,分别持有秘密
a
,
b
∈
Z
q
a, b \in \mathbb{Z}_q
a,b∈Zq,可以将它们视为秘密
x
=
a
b
m
o
d
q
x = ab \mod q
x=abmodq 的乘法份额。Alice 和 Bob 希望计算
x
x
x 的加法份额随机值
α
,
β
\alpha, \beta
α,β,满足
α
+
β
=
x
=
a
b
m
o
d
q
\alpha + \beta = x = ab \mod q
α+β=x=abmodq,其中 Alice 持有
α
\alpha
α(和
a
a
a),Bob 持有
β
\beta
β(和
b
b
b)。
本文提出一个基于加法同态加密方案的协议,这种方案在文献中多次出现(如 [9, 26, 28, 30]),但本文将其适配为当前需求。假设 Alice 关联了一个加法同态加密方案
E
\mathcal{E}
E 的公钥
E
A
E_A
EA,该方案定义在一个整数
N
N
N 上。以下称此协议为从乘法到加法的份额转换协议(MtA,Multiplicative to Additive)。此外,假设
B
=
g
b
B = g^b
B=gb 是公开的,在这种情况下,Bob 会额外检查以确保其使用的
b
b
b 是正确的。增强后的协议称为 MtAwc(”MtA with check”)。
份额转换协议步骤为:
- 1)Alice 启动协议:
- 发送 c A = E A ( a ) c_A = E_A(a) cA=EA(a) 给 Bob。
- 通过零知识证明(ZK)证明她知道 a < q 3 a < q^3 a<q3,通过范围证明实现。
- 2)Bob 计算:
c B = b × E c A + E E A ( β ′ ) = E A ( a b + β ′ ) c_B = b \times_E c_A +_E E_A(\beta') = E_A(ab + \beta') cB=b×EcA+EEA(β′)=EA(ab+β′)
其中 β ′ \beta' β′ 是在 Z q 5 \mathbb{Z}_{q^5} Zq5 中均匀随机选择的。Bob 设置他的份额为 β = − β ′ m o d q \beta = -\beta' \mod q β=−β′modq。然后他回应 Alice:- 发送 c B c_B cB。
- 通过零知识证明(ZK)证明他知道
b
<
q
3
b < q^3
b<q3,
β
′
<
q
7
\beta' < q^7
β′<q7,使得
c B = b × E c A + E E A ( β ′ ) c_B = b \times_E c_A +_E E_A(\beta') cB=b×EcA+EEA(β′) - 如果 B = g b B = g^b B=gb 是公开的,还需证明 B = g b B = g^b B=gb。
- 3)Alice 解密:
Alice 解密 c B c_B cB 得到 α ′ \alpha' α′,并设置 α = α ′ m o d q \alpha = \alpha' \mod q α=α′modq。
正确性:
假设双方都是诚实的,且
N
>
q
8
N > q^8
N>q8。注意到 Alice 解密得到的值为
α
′
=
a
b
+
β
′
m
o
d
N
\alpha' = ab + \beta' \mod N
α′=ab+β′modN。由于
β
′
<
N
−
a
b
\beta' < N - ab
β′<N−ab,因此不会发生模
N
N
N 的归约操作,意味着协议正确地计算出
α
,
β
\alpha, \beta
α,β 使得
α
+
β
=
x
m
o
d
q
\alpha + \beta = x \mod q
α+β=xmodq。
模拟性:
- 1)如果攻击者腐化了 Alice,则 Bob 的消息可以在不知道输入 b b b 的情况下进行模拟。模拟器可以随机选择 b ′ ∈ Z q b' \in \mathbb{Z}_q b′∈Zq 并充当 Bob。由于“噪声” β ′ \beta' β′ 在 Z q 5 \mathbb{Z}_{q^5} Zq5 中均匀分布,Alice 解密得到的消息分布与 Bob 使用真实 b b b 的情况统计上接近。
- 2)如果攻击者腐化了 Bob,则 Alice 的消息可以在不知道输入 a a a 的情况下进行模拟。模拟器可以随机选择 a ′ ∈ Z q a' \in \mathbb{Z}_q a′∈Zq 并充当 Alice。此时,由于加密方案 E \mathcal{E} E 的语义安全性,Bob 的视图与真实情况在计算上无法区分。
然而,如果不使用范围证明,恶意的 Alice 或 Bob 可以通过选择较大的输入导致协议“失败”。作为一个独立协议,这并不成问题,因为双方甚至不知道是否发生了模
N
N
N 的归约操作,且不会泄露关于另一方输入的信息。但在本门限DSA 协议中,这种攻击会导致签名验证失败,并且此信息与另一方输入的大小相关。
如,如果 Alice 使用输入
a
′
=
q
7
+
a
a' = q^7 + a
a′=q7+a 运行协议:
- 如果 Bob 的输入较小,则不会发生模 N N N 的归约,协议成功,且最终的签名在门限DSA 协议中可以验证(因为 a ′ = a m o d q a' = a \mod q a′=amodq)。
- 如果 Bob 的输入较大,则协议会失败。
类似问题也会出现在未检查
b
b
b 和
β
′
\beta'
β′ 范围的情况下。
因此,在存在一个告诉双方是否发生模
N
N
N 归约的“预言机”时,需要保证安全性。但由于零知识范围证明,这种归约仅以可忽略的概率发生,因此协议仍然安全。
备注:
- 关于零知识证明和模数
N
N
N 的大小:
协议中所需的零知识证明使用了类似于 [30](以及已在 [17] 中使用)的简化版本。其安全性依赖于强 RSA 假设。此外,这些证明需要 N ≈ q 8 N \approx q^8 N≈q8,如上所述。对于典型的参数选择, N ≈ q 8 N \approx q^8 N≈q8(因为 q q q 通常为 256 位,而 N N N 是一个 2048 位的 RSA 模数),因此这一要求并不成问题。然而,参与方必须检查 N N N 的大小,以确保证明的零知识属性。- 对于证明 a , b < K a, b < K a,b<K 的简单范围证明,可以选择使用 Boudot 证明的变体 [5],该变体设定 K ∼ q K \sim q K∼q,从而使得 N ∼ q 3 N \sim q^3 N∼q3。这个证明的效率不如 [17, 30] 中的那些证明,而后者在 MtAwc 协议中是 Bob 所需要的。更重要的是,正如之前所说,实际情况中 N > q 8 N > q^8 N>q8,因此 N N N 的大小改进对 ECDSA 并无实质性影响。
- 关于之前版本的说明:
在之前的版本中,从 Z N \mathbb{Z}_N ZN 中均匀随机选择 β ′ \beta' β′,且未对其施加范围检查。这导致了类似的信息泄露问题,如若选择的 β ′ \beta' β′ 接近 N N N,归约操作(进而协议失败)会根据输入 a a a 的分布发生。
2. GG18门限多签方案
所有参与者以DSA签名方案中使用的循环群 G , g G, g G,g 为输入。假设每个参与者 P i P_i Pi 关联了一个用于加法同态加密方案 E \mathcal{E} E 的公钥 E i E_i Ei 。
2.1 密钥生成协议
密钥生成协议分为3个阶段:
- 1)阶段1
每个参与者 P i P_i Pi 随机选择 u i ∈ R Z q u_i \in_R \mathbb{Z}_q ui∈RZq ,计算 [ K G C i , K G D i ] = Com ( g u i ) [KGC_i, KGD_i] = \text{Com}(g^{u_i}) [KGCi,KGDi]=Com(gui) 并广播 K G C i KGC_i KGCi 。每个参与者 P i P_i Pi 广播Paillier密码系统的公钥 E i E_i Ei 。 - 2)阶段2
每个参与者 P i P_i Pi 广播 K G D i KGD_i KGDi ,记 y i y_i yi 为 P i P_i Pi decommitted解承诺后的值。参与者 P i P_i Pi 对 u i u_i ui 进行 ( t , n ) (t, n) (t,n) 的 Feldman-VSS,其中 y i y_i yi 是“指数中的自由项”。公钥设为 y = ∏ i y i y = \prod_i y_i y=∏iyi 。每个参与者将从 n n n 个 Feldman-VSS 协议中接收到的私有份额相加,得到的值 x i x_i xi 是私钥 x = ∑ i u i x = \sum_i u_i x=∑iui 的 ( t , n ) (t, n) (t,n) Shamir秘密份额。此外,公开值为 X i = g x i X_i = g^{x_i} Xi=gxi 。 - 3)阶段3
令 N i = p i q i N_i = p_i q_i Ni=piqi 为与 E i E_i Ei 相关的RSA模数。每个参与者 P i P_i Pi 使用 Schnorr 协议(见CP Schnorr 1991年论文《Efficient signature generation by smart cards》)证明其知道 x i x_i xi ,并使用 Gennaro、Micciancio 和 Rabin 的算法(见1998年论文《An efficient non-interactive statistical zero-knowledge proof system for quasi-safe prime products》)来证明 N i N_i Ni 是square-free的。
其中:
- Com 是承诺算法。给定输入 pk \text{pk} pk 和消息 M M M,它输出 [ C ( M ) , D ( M ) ] = Com ( pk , M , r ) [C(M), D(M)] = \text{Com}(\text{pk}, M, r) [C(M),D(M)]=Com(pk,M,r),其中 r r r 是随机投掷的结果。 C ( M ) C(M) C(M) 是承诺字符串,而 D ( M ) D(M) D(M) 是解承诺字符串,在揭示之前保持秘密。
2.2 签名生成
签名生成协议中以消息
M
M
M 的哈希值
m
m
m 和上述密钥生成协议的输出为输入。注意,该协议为一个
t
t
t -out-of-
n
n
n 协议(且私钥
x
x
x 是使用
(
t
,
n
)
(t, n)
(t,n) Shamir秘密共享的)。
令
S
⊆
[
1..
n
]
S \subseteq [1..n]
S⊆[1..n] 表示参与签名协议的参与者集合,假设
∣
S
∣
=
t
+
1
|S| = t+1
∣S∣=t+1 。对于签名协议,可以使用
(
t
,
t
+
1
)
(t, t+1)
(t,t+1) 秘密共享方案分发任意ephemeral瞬时秘密,而无需使用
(
t
,
n
)
(t, n)
(t,n) 结构。注意,通过适当的Lagrangian系数
λ
i
,
S
\lambda_{i,S}
λi,S ,集合
S
S
S 中的每个参与者可以局部地将其
(
t
,
n
)
(t, n)
(t,n) 份额
x
i
x_i
xi 映射为
x
x
x 的
(
t
,
t
+
1
)
(t, t+1)
(t,t+1) 份额
w
i
=
(
λ
i
,
S
)
(
x
i
)
w_i = (\lambda_{i,S})(x_i)
wi=(λi,S)(xi) ,即
x
=
∑
i
∈
S
w
i
x = \sum_{i \in S} w_i
x=∑i∈Swi 。由于
X
i
=
g
x
i
X_i = g^{x_i}
Xi=gxi 和
λ
i
,
S
\lambda_{i,S}
λi,S 是公开的,所有参与者都可以计算
W
i
=
g
w
i
=
∏
i
X
i
λ
i
,
S
W_i = g^{w_i} = \prod_i X_i^{\lambda_{i,S}}
Wi=gwi=∏iXiλi,S 。
签名生成协议共分为5个阶段:
- 1)阶段 1:
每个玩家 P i P_i Pi 随机选择 k i , γ i ∈ R Z q k_i, \gamma_i \in_R \mathbb{Z}_q ki,γi∈RZq ,计算 [ C i , D i ] = Com ( g γ i ) [C_i, D_i] = \text{Com}(g^{\gamma_i}) [Ci,Di]=Com(gγi) 并广播 C i C_i Ci 。
定义: k = ∑ i ∈ S k i , γ = ∑ i ∈ S γ i k = \sum_{i \in S} k_i, \quad \gamma = \sum_{i \in S} \gamma_i k=∑i∈Ski,γ=∑i∈Sγi
注意有:
k γ = ∑ i , j ∈ S k i γ j m o d q k\gamma = \sum_{i,j \in S} k_i \gamma_j \mod q kγ=∑i,j∈Skiγjmodq
k x = ∑ i , j ∈ S k i w j m o d q kx = \sum_{i,j \in S} k_i w_j \mod q kx=∑i,j∈Skiwjmodq - 2)阶段 2:
每对玩家 P i , P j P_i, P_j Pi,Pj 参与两轮multiplicative-to-additive的份额转换子协议:- 2.1)子协议1:
玩家 P i , P j P_i, P_j Pi,Pj 分别使用 k i k_i ki 和 γ j \gamma_j γj 的份额运行 MtA 协议。令 α i j \alpha_{ij} αij ( β i j \beta_{ij} βij )表示玩家 P i P_i Pi ( P j P_j Pj )在协议结束时收到的份额,即
k i γ j = α i j + β i j k_i \gamma_j = \alpha_{ij} + \beta_{ij} kiγj=αij+βij
玩家 P i P_i Pi 设置: δ i = k i γ i + ∑ j ≠ i α i j + ∑ j ≠ i β j i \delta_i = k_i \gamma_i + \sum_{j \neq i} \alpha_{ij} + \sum_{j \neq i} \beta_{ji} δi=kiγi+∑j=iαij+∑j=iβji
注意, δ i \delta_i δi 是 k γ = ∑ i ∈ S δ i k\gamma = \sum_{i \in S} \delta_i kγ=∑i∈Sδi 的 ( t , t + 1 ) (t, t+1) (t,t+1) additive sharing加法份额。 - 2.2)子协议2:
玩家 P i , P j P_i, P_j Pi,Pj 分别使用 k i k_i ki 和 w j w_j wj 的份额运行 MtAwc 协议。令 μ i j \mu_{ij} μij ( ν i j \nu_{ij} νij )表示玩家 P i P_i Pi ( P j P_j Pj )在协议结束时收到的份额,即
k i w j = μ i j + ν i j k_i w_j = \mu_{ij} + \nu_{ij} kiwj=μij+νij
玩家 P i P_i Pi 设置: σ i = k i w i + ∑ j ≠ i μ i j + ∑ j ≠ i ν j i \sigma_i = k_i w_i + \sum_{j \neq i} \mu_{ij} + \sum_{j \neq i} \nu_{ji} σi=kiwi+∑j=iμij+∑j=iνji
注意, σ i \sigma_i σi 是 k x = ∑ i ∈ S σ i kx = \sum_{i \in S} \sigma_i kx=∑i∈Sσi 的 ( t , t + 1 ) (t, t+1) (t,t+1) 加法份额。
- 2.1)子协议1:
- 3)阶段 3:
每个玩家 P i P_i Pi 广播 δ i \delta_i δi ,且所有玩家重构: δ = ∑ i ∈ S δ i = k γ \delta = \sum_{i \in S} \delta_i = k\gamma δ=∑i∈Sδi=kγ,然后计算 δ − 1 m o d q \delta^{-1} \mod q δ−1modq 。 - 4)阶段 4:
每个玩家 P i P_i Pi 广播 D i D_i Di 。令 Γ i \Gamma_i Γi 表示玩家 P i P_i Pi decommitted解承诺值, P i P_i Pi 通过零知识证明(ZK)证明其知道 γ i \gamma_i γi ,使得 Γ i = g γ i \Gamma_i = g^{\gamma_i} Γi=gγi (使用 Schnorr 协议)。玩家们计算:
R = [ ∏ i ∈ S Γ i ] δ − 1 = g ( ∑ i ∈ S γ i ) k − 1 γ − 1 = g k − 1 R = \left[\prod_{i \in S} \Gamma_i\right]^{\delta^{-1}} = g^{\left(\sum_{i \in S} \gamma_i\right)k^{-1}\gamma^{-1}} = g^{k^{-1}} R=[∏i∈SΓi]δ−1=g(∑i∈Sγi)k−1γ−1=gk−1
以及 r = H ′ ( R ) r = H'(R) r=H′(R) 。 - 5)阶段 5:
每个玩家 P i P_i Pi 设置: s i = m k i + r σ i s_i = m k_i + r \sigma_i si=mki+rσi。注意:
∑ i ∈ S s i = m ∑ i ∈ S k i + r ∑ i ∈ S σ i = m k + r k x = k ( m + x r ) = s \sum_{i \in S} s_i = m \sum_{i \in S} k_i + r \sum_{i \in S} \sigma_i = mk + rkx = k(m + xr) = s ∑i∈Ssi=m∑i∈Ski+r∑i∈Sσi=mk+rkx=k(m+xr)=s
即 s i s_i si 是 s s s 的 ( t , t + 1 ) (t, t+1) (t,t+1) 加法共享。- 5.1)(5A) 玩家
P
i
P_i
Pi 随机选择
ℓ
i
,
ρ
i
∈
R
Z
q
\ell_i, \rho_i \in_R \mathbb{Z}_q
ℓi,ρi∈RZq ,计算:
V i = R s i g ℓ i , A i = g ρ i , [ C ^ i , D ^ i ] = Com ( V i , A i ) V_i = R^{s_i} g^{\ell_i}, \quad A_i = g^{\rho_i}, \quad [\hat{C}_i, \hat{D}_i] = \text{Com}(V_i, A_i) Vi=Rsigℓi,Ai=gρi,[C^i,D^i]=Com(Vi,Ai)
并广播 C ^ i \hat{C}_i C^i 。定义 ℓ = ∑ i ℓ i , ρ = ∑ i ρ i \ell = \sum_i \ell_i, \quad \rho = \sum_i \rho_i ℓ=∑iℓi,ρ=∑iρi 。 - 5.2)(5B) 玩家
P
i
P_i
Pi 广播
D
^
i
\hat{D}_i
D^i ,并通过零知识证明(ZK)证明其知道
s
i
,
ℓ
i
,
ρ
i
s_i, \ell_i, \rho_i
si,ℓi,ρi ,满足:
V
i
=
R
s
i
g
ℓ
i
,
A
i
=
g
ρ
i
V_i = R^{s_i} g^{\ell_i}, \quad A_i = g^{\rho_i}
Vi=Rsigℓi,Ai=gρi 。
如果零知识证明失败,协议终止。
令 V = g − m y − r ∏ i ∈ S V i , A = ∏ i ∈ S A i V = g^{-m} y^{-r} \prod_{i \in S} V_i, \quad A = \prod_{i \in S} A_i V=g−my−r∏i∈SVi,A=∏i∈SAi
其中应有: V = g ℓ V=g^{\ell} V=gℓ 。 - 5.3)(5C) 玩家
P
i
P_i
Pi 计算:
U
i
=
V
ρ
i
,
T
i
=
A
ℓ
i
U_i = V^{\rho_i}, \quad T_i = A^{\ell_i}
Ui=Vρi,Ti=Aℓi
并承诺 [ C ~ i , D ~ i ] = Com ( U i , T i ) [\tilde{C}_i, \tilde{D}_i] = \text{Com}(U_i, T_i) [C~i,D~i]=Com(Ui,Ti) ,然后广播 C ~ i \tilde{C}_i C~i 。 - 5.4)(5D) 玩家
P
i
P_i
Pi 广播
D
i
D_i
Di ,decommitted解承诺
U
i
,
T
i
U_i, T_i
Ui,Ti 。如果
∏ i ∈ S T i ≠ ∏ i ∈ S U i \prod_{i \in S} T_i \neq \prod_{i \in S} U_i ∏i∈STi=∏i∈SUi
则协议终止。 - 5.5)(5E) 否则,玩家 P i P_i Pi 广播 s i s_i si ,各玩家计算 s = ∑ i ∈ S s i s = \sum_{i \in S} s_i s=∑i∈Ssi 。如果 ( r , s ) (r, s) (r,s) 不是有效签名,则协议终止,否则协议完成。
- 5.1)(5A) 玩家
P
i
P_i
Pi 随机选择
ℓ
i
,
ρ
i
∈
R
Z
q
\ell_i, \rho_i \in_R \mathbb{Z}_q
ℓi,ρi∈RZq ,计算:
阶段5的直观解释:
为了避免昂贵的零知识证明(ZK),可能会先构造一个错误的签名,对其进行验证后再决定是否接受或拒绝。一种简单的实现方式是让玩家直接公开
s
i
s_i
si ,然后重构
s
=
∑
i
s
i
s = \sum_i s_i
s=∑isi 。但是,这种方式无法在证明中被验证为安全,原因直观上可以理解为:如果对手通过输出无效签名使协议失败,那么他可能从良性玩家持有的
s
i
s_i
si 中获取有价值的信息。
一种直接的改进是让玩家先广播
S
i
=
R
s
i
S_i = R^{s_i}
Si=Rsi ,然后根据 DSA 验证算法检查
∏
i
S
i
=
R
s
=
g
m
y
r
\prod_i S_i = R^s = g^{m} y^r
∏iSi=Rs=gmyr 。然而,由于类似原因,这一步同样会导致证明失败。
因此,在本协议中,玩家用随机值
g
ℓ
i
g^{\ell_i}
gℓi 对
R
s
i
R^{s_i}
Rsi 进行掩码。令
V
i
=
R
s
i
g
ℓ
i
V_i = R^{s_i} g^{\ell_i}
Vi=Rsigℓi
则
∏
i
V
i
=
R
s
g
ℓ
\prod_i V_i = R^s g^{\ell}
∏iVi=Rsgℓ
从而
V
=
g
ℓ
V = g^{\ell}
V=gℓ 。玩家不能直接公开
g
ℓ
i
g^{\ell_i}
gℓi 来验证
V
V
V 的正确性,因为这会“de-mask去掩码”
R
s
i
R^{s_i}
Rsi 。因此,通过一个分布式的“Diffie-Hellman”交换机制对聚合值进行随机化,得到
U
=
g
ℓ
ρ
U = g^{\ell \rho}
U=gℓρ
同时玩家分布式计算
g
ℓ
ρ
g^{\ell \rho}
gℓρ 。
如果这一分布式随机化签名验证成功,则可以安全地公开各自的
s
i
s_i
si 。但如果签名验证失败,协议会立即终止,良性玩家持有的
s
i
s_i
si 不会被明文泄露。
2.3 零知识证明
在步骤 (5B) 中,玩家 P P P 输出 V = R s g ℓ V = R^s g^{\ell} V=Rsgℓ 和 A = g ρ A = g^{\rho} A=gρ ,并证明其知道满足以下关系的 s , ℓ , ρ s, \ell, \rho s,ℓ,ρ 。
- A = g ρ A = g^{\rho} A=gρ 的证明使用经典的 Schnorr 协议。
- 对于
V
V
V ,可以使用以下经典(honest-verifier)ZK proof:
- Prover选择 a , b ∈ R Z q a, b \in_R \mathbb{Z}_q a,b∈RZq ,发送 α = R a g b \alpha = R^a g^b α=Ragb 。
- Verifier选择随机挑战 c ∈ R Z q c \in_R \mathbb{Z}_q c∈RZq 。
- Prover响应 t = a + c s m o d q t = a + cs \mod q t=a+csmodq 和 u = b + c ℓ m o d q u = b + c\ell \mod q u=b+cℓmodq 。
- Verifier检查: R t g u = α V c R^t g^u = \alpha V^c Rtgu=αVc
3. 代码说明
https://github.com/bnb-chain/tss-lib(Go)中实现了:
- GG18论文的多方
(
t
,
n
)
(t, n)
(t,n) -门限ECDSA(Elliptic Curve Digital Signature Algorithm)签名方案:
- ECDSA 广泛用于比特币、以太坊(secp256k1 曲线)、NEO(NIST P-256 曲线)等加密货币。
- 以及采用类似方法的 多方
(
t
,
n
)
(t, n)
(t,n) -门限EdDSA(Edwards-curve Digital Signature Algorithm)签名方案:
- EdDSA 广泛用于 Cardano、Aeternity、Stellar Lumens 等加密货币。
其中包含了3个协议:
- 1)keygen协议:用于创建没有可信dealer的秘密共享的密钥生成。
- 2)signing协议:即使用秘密份额生成签名。
- 3)resharing协议:动态组可以在保持秘密的同时改变参与者的组。
对于使用ECDSA和EdDSA的token,此技术可用于创建加密钱包,其中多方必须合作签署交易。
每个参与者在本地存储每个密钥/地址的秘密份额,这些份额由协议保证安全——任何时候都不会向其他人透露。此外,这些份额没有值得信赖的经销商。
与多签解决方案相比:
- TSS(门限签名) 生成的交易不会透露哪些 t + 1 t+1 t+1个参与者 参与了签名,从而保护了签名者的隐私。
- 还有一个性能奖励,即区块链节点可以检查签名的有效性,而无需任何额外的 MultiSig 逻辑或处理。
为使用https://github.com/bnb-chain/tss-lib(Go),首先应创建一个LocalParty
实例,并为其提供所需的参数,其来自于keygen
、signing
还是sharing
取决于具体需要。
3.1 Setup
// When using the keygen party it is recommended that you pre-compute the "safe primes" and Paillier secret beforehand because this can take some time.
// This code will generate those parameters using a concurrency limit equal to the number of available CPU cores.
preParams, _ := keygen.GeneratePreParams(1 * time.Minute)
// Create a `*PartyID` for each participating peer on the network (you should call `tss.NewPartyID` for each one)
parties := tss.SortPartyIDs(getParticipantPartyIDs())
// Set up the parameters
// Note: The `id` and `moniker` fields are for convenience to allow you to easily track participants.
// The `id` should be a unique string representing this party in the network and `moniker` can be anything (even left blank).
// The `uniqueKey` is a unique identifying key for this peer (such as its p2p public key) as a big.Int.
thisParty := tss.NewPartyID(id, moniker, uniqueKey)
ctx := tss.NewPeerContext(parties)
// Select an elliptic curve
// use ECDSA
curve := tss.S256()
// or use EdDSA
// curve := tss.Edwards()
params := tss.NewParameters(curve, ctx, thisParty, len(parties), threshold)
// You should keep a local mapping of `id` strings to `*PartyID` instances so that an incoming message can have its origin party's `*PartyID` recovered for passing to `UpdateFromBytes` (see below)
partyIDMap := make(map[string]*PartyID)
for _, id := range parties {
partyIDMap[id.Id] = id
}
3.2 keygen
keygen协议中使用keygen.LocalParty
,在keygen协议之后,通过endCh
收到的数据应保存在安全存储中:
party := keygen.NewLocalParty(params, outCh, endCh, preParams) // Omit the last arg to compute the pre-params in round 1
go func() {
err := party.Start()
// handle err ...
}()
3.3 signing
对消息message
,使用signing.LocalParty
进行签名,需要从 keygen 协议获取的密钥数据。签名完成后将通过endCh
发送。
注意, t + 1 t+1 t+1个签名者必须签署消息,为了达到最佳使用效果,不再需要更多的签名者参与其中。每个签名者对 t + 1 t+1 t+1个签名者的身份应有相同的看法。
party := signing.NewLocalParty(message, params, ourKeyData, outCh, endCh)
go func() {
err := party.Start()
// handle err ...
}()
3.4 resharing
使用resharing.LocalParty
重新分配秘密份额。通过endCh
收到的保存数据应覆盖存储中的现有密钥数据,或者如果该方正在接收新份额,则写入新数据。
注意,ReSharingParameters
用于向该方提供更多有关应进行的重新共享的上下文信息。
party := resharing.NewLocalParty(params, ourKeyData, outCh, endCh)
go func() {
err := party.Start()
// handle err ...
}()
注意:
- 在重新共享过程中,密钥数据可能会在轮次中被修改。在通过
endCh
通道接收到最终结构之前,切勿覆盖磁盘上保存的任何数据。
3.5 消息传递
outCh
将收集来自一方的传出消息,并且当协议完成时endCh
将接收保存的数据或签名。
在协议期间,应该向该方提供从网络上其他参与方收到的更新。
Party
有2种线程安全的方法来接收更新:
// The main entry point when updating a party's state from the wire
UpdateFromBytes(wireBytes []byte, from *tss.PartyID, isBroadcast bool) (ok bool, err *tss.Error)
// You may use this entry point to update a party's state when running locally or in tests
Update(msg tss.ParsedMessage) (ok bool, err *tss.Error)
且tss.Message
具有以下两种为wire将消息转换为数据的方法:
// Returns the encoded message bytes to send over the wire along with routing information
WireBytes() ([]byte, *tss.MessageRouting, error)
// Returns the protobuf wrapper message struct, used only in some exceptional scenarios (i.e. mobile apps)
WireMsg() *tss.MessageWrapper
在典型的用例中,预计传输实现将通过本地Party的out
通道使用消息字节,将它们发送到msg.GetTo()
的结果中指定的目的地,并将它们传递给UpdateFromBytes
接收端。
这样,就不需要处理 Marshal/Unmarshalling
协议缓冲区来实现传输。
3.5 安全说明
消息传输留给应用层,不由此库提供。应仔细阅读并遵循以下每一段,因为实施安全传输以确保协议安全至关重要:
- 当构建传输时,应该提供广播通道以及连接每对参与方的点对点通道。传输还应在参与方之间采用适当的端到端加密(建议使用带有AEAD加密的 TLS ),以确保参与方只能读取发送给它的消息。
- 在传输过程中,每条消息都应使用会话 ID进行封装,该 ID 对于密钥生成器、签名或重新共享轮次的单次运行是唯一的。此会话 ID 应在带外达成一致,并且只有参与方在轮次开始前才知道。在收到任何消息时,应用程序应确保收到的会话 ID 与开始时达成一致的会话 ID 相匹配。
- 此外,传输中应该有一个机制来允许“可靠广播”,这意味着各方可以向其他方广播消息,从而保证每个人都收到相同的消息。网上有几个算法示例,它们通过共享和比较收到的消息的哈希值来实现这一点。
- 应用程序应处理超时和错误。可以在
Party
上调用WaitingFor
方法以获取仍在等待消息的其他方集。 还可以从*tss.Error
中获取导致错误的罪魁祸首集。