声明: 本文纯做技术分享, 不代表任何加密货币投资购买建议
背景: 在之前的加密货币安全基石: 分布式密钥生成协议(GG18, 币安tss-lib keygen部分) 中,我向大家展示了如何进行分布式的密钥生成。在这篇文章中,我将在上篇文章基础更进一步, 讲解如何利用keygen过程中的生成的Paillier 同态加密公私钥对进行门线签名的关键步骤 MTA(multiplicative to additive)协议。 我将从ECDSA签名算法开始,讲解为什么需要用到 MTA协议, MTA协议与Paillier同态加密的关系。最后以币安 tss lib 的单测函数为例,简单过一下MTA协议的golang实现。
需要你有一些密码学, 加密货币, 区块链的前置知识:
- 使用 golang 实现一个极简的 区块链
- 加密货币安全基石:直观理解ECC椭圆曲线加密算法
- 加密货币安全基石:从ECC到ECDSA椭圆曲线签名
- 加密货币安全基石: 从拉格朗日插值到shamir共享密钥算法
- 加密货币安全基石: 从Shamir到Feldman VSS算法, 以币安tss-lib库为例
- 加密货币安全基石: 详解 Paillier 同态加密并使用币安tss-lib库
- 加密货币安全基石: 分布式密钥生成协议(GG18, 币安tss-lib keygen部分)
回顾 ECDSA 签名
ECDSA 的详细原理中我之前的 加密货币安全基石:从ECC到ECDSA椭圆曲线签名 中有详细介绍, 这里我们简单回顾一下即可。
定义 G为椭圆曲线 起点, g 为循环群, 私钥 为 d, 公钥Q=dG, n 为 一个大质数, 被签名的原文为 M , 其中 G,Q,M,n 公开, d 为签名方严格保密 签名过程如下:
- 对原文进行取哈希操作, 令
- 从 范围内选一个随机数 k, 计算终点 , R 的 横坐标记为 r
- 计算 , 其中 为 的逆元
- 最终签名由两部分组成
如果要想去中心化地签名, 就需要去中心化地计算 r 和 s。
MTA协议与计算 ECDSA 签名中的 r 有何关系
我们知道r就是R的横坐标, 想要拆分r实际上就是拆分R. 我们不妨将R虚拟地进行进行如下拆分:
其中 是随机数, 也是随机数,
我们继续对 进行拆分, 将 其拆成 i 份, 其中 i 为 私钥分片的个数。则上式子可以写成
假设 i=2 则上式可以写成:
我们观察上式, 由每一个 party 私自持有, 而 公开, 剩下的问题上如何去中心化地协商出
我们继续拆分 k , 将 k 也拆成 i 份, i 为 私钥分片数量。假设私钥分片数量为2, 那么 可以写成:
仔细观察上式, 可以看到 由 party1(分片1的持有方) 完全掌握, 由 party2(分片2的持有方) 完全掌握, 剩下的问题就变成了 party1 和 party2 如何在不暴露自己的 和 情况下计算出 和
我们将 拆成两个数字的和比如:
将 拆成两个数字的和比如:
有没有一种方法可以在part1不泄露 party2 不泄露 的情况下使得 party1 得到 A,C party2 得到 B,C 最终得到
答案是有的, 就是 MTA(multiplicative to additive)协议 。
MTA 协议
MTA 协议需要两个参与方, 命名为 Alice 和 Bob。 其中Alice 拥有 Paillier 公私钥对, 拥有 秘密值 a. Bob 拥有 秘密值 b 和随机数 .
MTA协议可以做到在 Alice 不泄露 a 的情况下, 在 Bob 不泄露 b 的情况下, 让 Alice得到一个数 , 让 Bob 得到一个数字 使得下列等式成立:
这个性质是不是很熟悉, 和上面我们拆分 时需要计算的 的情景是一致的, 需要两方保持各自的秘密, 但是又可以找到两个值通过相加 最终的得到的结果和秘密值相乘 是等价的。
下面来看看 MTA 协议是如何通过 Paillier 同态加密实现的。
-
Alice 像 Bob 发送 cA= E_A(a), E_A, 其中 E_a 为 Alice 的 Paillier 公钥, E_A(a)表示使用 Paillier公钥对其秘密值 a 进行加密。
-
Bob 准备一个随机数 , 然后计算 , 令
-
Bob 向 Alice 发送
-
Alice 解密 , , 令
然后我们计算
就会发现各自秘密值 ab的乘值 已经转换成了各自秘密值 和 .
我们把这个原理运用到 计算 就是这样的:
- party1 对 party2 发起 MTA 协议, part1 使用 , part2 使用 作为各自的秘密值, 最终party1和part2得到交换后的秘密值 A,B.
- 同上面一样, 不过是party2 对 party1 发起 MTA 协议, part2 使用 , part1 使用 作为各自的秘密值, 最终party2和part1得到交换后的秘密值 C,D.
也就是说每一个私钥分片持有方两两进行两次MTA就可以完成对ECDSA签名中R的拆分。
币安 tss lib 中的 MTA 协议
币安 tsslib 中 MTA 协议的函数位于 tss-lib/crypto/mta/share_protocol_test.go 中, 相关注释都已经写好了。 特别的是用到了额外的 range proof, 如果不使用 range proof 则存在可被攻击的漏洞, 通过多次签名即可盗取私钥, 具体可参考这篇文章Alpha-Rays: Key Extraction Attacks on Threshold ECDSA Implementations
func TestShareProtocol(t *testing.T) {
q := tss.EC().Params().N
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()
// paillier 公私钥对
sk, pk, err := paillier.GenerateKeyPair(ctx, rand.Reader, testPaillierKeyLength)
assert.NoError(t, err)
a := common.GetRandomPositiveInt(rand.Reader, q)
b := common.GetRandomPositiveInt(rand.Reader, q)
// MTA 协议额外的 range proof
NTildei, h1i, h2i, err := keygen.LoadNTildeH1H2FromTestFixture(0)
assert.NoError(t, err)
NTildej, h1j, h2j, err := keygen.LoadNTildeH1H2FromTestFixture(1)
assert.NoError(t, err)
// Alice 发送给 Bob
cA, pf, err := AliceInit(tss.EC(), pk, a, NTildej, h1j, h2j, rand.Reader)
assert.NoError(t, err)
// Bob 计算 beta
_, cB, betaPrm, pfB, err := BobMid(Session, tss.EC(), pk, pf, b, cA, NTildei, h1i, h2i, NTildej, h1j, h2j, rand.Reader)
assert.NoError(t, err)
// Alice 计算 alpha
alpha, err := AliceEnd(Session, tss.EC(), pk, pfB, h1i, h2i, cA, cB, NTildei, sk)
assert.NoError(t, err)
// 验证 alpha + beta = a +b mod q
// expect: alpha = ab + betaPrm
aTimesB := new(big.Int).Mul(a, b)
aTimesBPlusBeta := new(big.Int).Add(aTimesB, betaPrm)
aTimesBPlusBetaModQ := new(big.Int).Mod(aTimesBPlusBeta, q)
assert.Equal(t, 0, alpha.Cmp(aTimesBPlusBetaModQ))
}
总结
这篇文章中,我向大家推导了如何拆分ECDSA签名中的r部分, 关键步骤是使用基于Paillier同态加密的MTA协议, 最后简单过来一下币安tsslib中的MTA协议。 ECDSA签名由r和s两部分组成, 我会以后更新如何拆分其中的s部分 , 欢迎大家点赞, 收藏, 评论, 关注