40-MPC钱包01
探索MPC钱包:数字资产安全的革命性创新
随着数字货币的普及,资产安全成为用户关注的焦点。传统的钱包依赖于单一私钥,存在单点故障的风险。MPC钱包的出现,以其独特的多方计算技术,为数字资产的安全存储和交易提供了新的解决方案。
MPC钱包的核心原理
MPC钱包的工作原理基于一个简单的概念:将私钥分割成多个碎片,每个碎片由不同的参与者持有。只有当足够数量的参与者共同参与计算时,才能恢复出完整的私钥,进而授权交易。
实现MPC钱包的关键技术
- 私钥分片:使用特定的算法,如Shamir’s Secret Sharing,将私钥分割成多个份额,每个份额本身不包含任何有关原始私钥的信息。
- 阈值签名:定义一个阈值,只有当达到或超过这个阈值数量的份额共同参与时,才能生成有效的签名。
- 多方计算协议:确保在不泄露各自份额的前提下,多个参与者可以协同计算出私钥的完整形式。
MPC钱包的优势
- 提高安全性:由于私钥的分散存储,即使部分份额被泄露,也不会导致资产损失。
- 降低用户门槛:用户无需管理复杂的私钥或助记词,简化了使用流程。
- 适应性强:MPC钱包适用于需要高度安全性的场景,如大额资产存储和金融交易。
okx开源库
参考链接:okx-threshold-lib,
提供了基于阈值签名方案的多方计算(MPC)实现。这个库是用 Go 语言编写的,支持 ECDSA 和 Ed25519 签名算法的阈值签名。
产品逻辑
- 结合okx-MPC钱包,总体将私钥分为三方: 用户设备、用户云盘、交易所,其中任意两方即可完成消息签名
- 紧急情况下可以通过用户云盘将私钥导出
算法实现
- ECDSA 阈值签名:使用 Feldman’s VSS(Verifiable Secret Sharing,可验证秘密共享)算法生成密钥份额,并使用 Lindell 17 协议实现两方签名。
- Ed25519 阈值签名:为 Ed25519 签名方案实现阈值签名功能。
- Bip32 密钥派生:支持密钥份额的非硬化派生,链码(chaincode)由多方共同生成。
- 密钥份额刷新:当一方的密钥份额丢失或有新的参与者加入时,支持密钥份额的刷新。
实现步骤
- 初始化:设置阈值参数和参与方数量。
- 密钥生成:使用秘密共享算法生成私钥的多个份额,并分配给不同的参与方。
- 密钥份额刷新:在密钥份额丢失或有新参与方加入时,重新分配或生成新的密钥份额,并更新现有份额。
- 聚合签名:在达到预设的阈值数量后,聚合各参与方的中间值,生成最终的签名。
- 签名验证:使用相应的公钥验证生成的签名是否有效。
代码实践
初始化:设置加密类型、阈值参数和参与方数量。
1
2
3// curve 决定采用哪种椭圆曲线来生成对应密钥
setUp1 := dkg.NewSetUp(1, 3, curve)密钥生成:使用秘密共享算法生成私钥的多个份额,并分配给不同的参与方。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55// KeyStep3Data 即 私钥的分片,示例是生成3个分片
func (k *keys) GenKey() (mpcKeys []*tss.KeyStep3Data, err error) {
setUp1 := dkg.NewSetUp(1, 3, k.curve)
setUp2 := dkg.NewSetUp(2, 3, k.curve)
setUp3 := dkg.NewSetUp(3, 3, k.curve)
msgs1_1, err := setUp1.DKGStep1()
if err != nil {
return nil, err
}
msgs2_1, err := setUp2.DKGStep1()
if err != nil {
return nil, err
}
msgs3_1, err := setUp3.DKGStep1()
if err != nil {
return nil, err
}
msgs1_2_in := []*tss.Message{msgs2_1[1], msgs3_1[1]}
msgs2_2_in := []*tss.Message{msgs1_1[2], msgs3_1[2]}
msgs3_2_in := []*tss.Message{msgs1_1[3], msgs2_1[3]}
msgs1_2, err := setUp1.DKGStep2(msgs1_2_in)
if err != nil {
return nil, err
}
msgs2_2, err := setUp2.DKGStep2(msgs2_2_in)
if err != nil {
return nil, err
}
msgs3_2, err := setUp3.DKGStep2(msgs3_2_in)
if err != nil {
return nil, err
}
msgs1_3_in := []*tss.Message{msgs2_2[1], msgs3_2[1]}
msgs2_3_in := []*tss.Message{msgs1_2[2], msgs3_2[2]}
msgs3_3_in := []*tss.Message{msgs1_2[3], msgs2_2[3]}
p1SaveData, err := setUp1.DKGStep3(msgs1_3_in)
if err != nil {
return nil, err
}
p2SaveData, err := setUp2.DKGStep3(msgs2_3_in)
if err != nil {
return nil, err
}
p3SaveData, err := setUp3.DKGStep3(msgs3_3_in)
if err != nil {
return nil, err
}
mpcKeys = append(mpcKeys, p1SaveData, p2SaveData, p3SaveData)
return mpcKeys, nil
}密钥份额刷新:在密钥份额丢失或有新参与方加入时,重新分配或生成新的密钥份额,并更新现有份额。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80func (k *keys) Refresh(key1, key2 *tss.KeyStep3Data) (mpcKeys []*tss.KeyStep3Data, err error) {
if key1.Id == key2.Id {
return nil, errors.New("key1 and key2 must be different")
}
devoteList := [2]int{key1.Id, key2.Id}
oldKeys := make([]*tss.KeyStep3Data, 3)
for i := 1; i <= 3; i++ {
if i == key1.Id {
oldKeys[i-1] = key1
} else if i == key2.Id {
oldKeys[i-1] = key2
} else {
oldKeys[i-1] = &tss.KeyStep3Data{
Id: i,
ShareI: nil,
PublicKey: key1.PublicKey,
ChainCode: key1.ChainCode,
SharePubKeyMap: nil,
}
}
}
refresh1 := reshare.NewRefresh(1, 3, devoteList, oldKeys[0].ShareI, oldKeys[0].PublicKey)
refresh2 := reshare.NewRefresh(2, 3, devoteList, oldKeys[1].ShareI, oldKeys[1].PublicKey)
refresh3 := reshare.NewRefresh(3, 3, devoteList, oldKeys[2].ShareI, oldKeys[2].PublicKey)
msgs1_1, err := refresh1.DKGStep1()
if err != nil {
return nil, err
}
msgs2_1, err := refresh2.DKGStep1()
if err != nil {
return nil, err
}
msgs3_1, err := refresh3.DKGStep1()
if err != nil {
return nil, err
}
msgs1_2_in := []*tss.Message{msgs2_1[1], msgs3_1[1]}
msgs2_2_in := []*tss.Message{msgs1_1[2], msgs3_1[2]}
msgs3_2_in := []*tss.Message{msgs1_1[3], msgs2_1[3]}
msgs1_2, err := refresh1.DKGStep2(msgs1_2_in)
if err != nil {
return nil, err
}
msgs2_2, err := refresh2.DKGStep2(msgs2_2_in)
if err != nil {
return nil, err
}
msgs3_2, err := refresh3.DKGStep2(msgs3_2_in)
if err != nil {
return nil, err
}
msgs1_3_in := []*tss.Message{msgs2_2[1], msgs3_2[1]}
msgs2_3_in := []*tss.Message{msgs1_2[2], msgs3_2[2]}
msgs3_3_in := []*tss.Message{msgs1_2[3], msgs2_2[3]}
p1SaveData, err := refresh1.DKGStep3(msgs1_3_in)
if err != nil {
return nil, err
}
p2SaveData, err := refresh2.DKGStep3(msgs2_3_in)
if err != nil {
return nil, err
}
p3SaveData, err := refresh3.DKGStep3(msgs3_3_in)
if err != nil {
return nil, err
}
mpcKeys = append(mpcKeys, p1SaveData, p2SaveData, p3SaveData)
for i := range mpcKeys {
mpcKeys[i].ChainCode = key1.ChainCode
}
return mpcKeys, nil
}聚合签名:在达到预设的阈值数量后,聚合各参与方的中间值,生成最终的签名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112// ECDSA签名示例
func (k *keys) Sign(key1, key2 *tss.KeyStep3Data, msg []byte, pariPrivate, preParams string) (string, string, error) {
if key1 == nil || key2 == nil {
return "", "", fmt.Errorf("key1 or key2 is nil")
}
if key1.Id == key2.Id {
return "", "", fmt.Errorf("key1.Id == key2.Id")
}
if key1.Id > key2.Id {
key1.Id = key2.Id
key2.Id = key1.Id
}
msgHash, err := Sha256Hash(msg)
if err != nil {
return "", "", fmt.Errorf("Sha256Hash error.err = %s", err.Error())
}
r, s, err := signMsgECDSA(key1, key2, msgHash, pariPrivate, preParams, k.curve)
if err != nil {
return "", "", fmt.Errorf("signMsgECDSA error.err = %s", err.Error())
}
return hex.EncodeToString(r.Bytes()), hex.EncodeToString(s.Bytes()), nil
}
func Sha256Hash(msg []byte) ([]byte, error) {
hash := sha256.New()
_, err := hash.Write(msg)
if err != nil {
return nil, fmt.Errorf("hash.Write(msgs) error, err = %s ", err.Error())
}
return hash.Sum(nil), nil
}
// signMsgECDSA 使用ECDSA算法签名消息。
// key1, key2: TSS密钥的第三步数据。
// msgHash: 需要签名的消息的哈希值。
// strPaillerKeys: Paillier公私钥对的字符串表示。
// strPreParams: 预计算参数的字符串表示。
// curve: 用于ECDSA的椭圆曲线。
// 返回签名的r和s值,以及可能的错误。
func signMsgECDSA(key1, key2 *tss.KeyStep3Data, msgHash []byte, strPaillerKeys, strPreParams string, curve elliptic.Curve) (*big.Int, *big.Int, error) {
// 根据传入的预计算参数字符串生成预计算参数对象。
var param *keygen.PreParams
if strPreParams == "" {
param = keygen.GeneratePreParams()
} else {
param = &keygen.PreParams{}
if err := json.Unmarshal([]byte(strPreParams), param); err != nil {
return nil, nil, fmt.Errorf("preParams Unmarshal error, err = %s ", err.Error())
}
}
// 根据传入的Paillier密钥字符串生成Paillier私钥对象。
var paiPrivateKey *paillier.PrivateKey
var err error
if strPaillerKeys == "" {
paiPrivateKey, _, err = paillier.NewKeyPair(8)
if err != nil {
return nil, nil, fmt.Errorf("paillier.NewKeyPair error, err = %s ", err.Error())
}
} else {
if err := json.Unmarshal([]byte(strPaillerKeys), paiPrivateKey); err != nil {
return nil, nil, fmt.Errorf("parillerKey Unmarshal error, err = %s ", err.Error())
}
}
// 执行P1步骤,生成P1输出数据。
p1Dto, errP := keygen.P1(key1.ShareI, paiPrivateKey, key1.Id, key2.Id, param)
if errP != nil {
return nil, nil, fmt.Errorf("keygen.P1 error, err = %s ", errP.Error())
}
// 将key2的公钥转换为EC点对象。
publicKey, err := curves.NewECPoint(curve, key2.PublicKey.X, key2.PublicKey.Y)
if err != nil {
return nil, nil, fmt.Errorf(" curves.NewECPoint pubKey error, err = %s ", err.Error())
}
// 执行P2步骤,生成P2保存数据。
p2SaveData, err := keygen.P2(key2.ShareI, publicKey, p1Dto, key1.Id, key2.Id)
if err != nil {
return nil, nil, fmt.Errorf("keygen.P2 error, err = %s ", err.Error())
}
//生成TSS密钥。 这部分应该是主钥
tssKey, err := bip32.NewTssKey(p2SaveData.X2, key2.PublicKey, key2.ChainCode)
if err != nil {
return nil, nil, fmt.Errorf("NewTssKey error, err = %s ", err.Error())
}
// 从TSS密钥中获取分享I值。
x2 := tssKey.ShareI()
// 创建ECDSA公钥对象。
tssPubKey := &ecdsa.PublicKey{Curve: curve, X: tssKey.PublicKey().X, Y: tssKey.PublicKey().Y}
// 初始化P1和P2签名阶段。
p1 := ecdsa_sign.NewP1(tssPubKey, hex.EncodeToString(msgHash), paiPrivateKey)
p2 := ecdsa_sign.NewP2(x2, p2SaveData.E_x1, tssPubKey, p2SaveData.PaiPubKey, hex.EncodeToString(msgHash))
// 执行P1和P2的Step1步骤。
commit, _ := p1.Step1()
bobProof, R2, _ := p2.Step1(commit)
// 执行P1的Step2步骤,使用P2的Step1输出。
proof, cmtD, _ := p1.Step2(bobProof, R2)
E_k2_h_xr, _ := p2.Step2(cmtD, proof)
// 执行P1的最后一步,生成签名的r和s值。
r, s, _ := p1.Step3(E_k2_h_xr)
return r, s, nil
}签名验证:使用相应的公钥验证生成的签名是否有效。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// test.go
keys, _ := tt.args.GenKey()
reKeys, _ := tt.args.Refresh(keys[0], keys[1])
got, got1, err := tt.args.Sign(reKeys[1], reKeys[2], []byte("hello"), "", "")
if (err != nil) != tt.wantErr {
t.Errorf("keys.Sign() error = %v, wantErr %v", err, tt.wantErr)
return
}
msgHash, _ := Sha256Hash([]byte("hello"))
r, _ := hex.DecodeString(got)
s, _ := hex.DecodeString(got1)
// verify ecdsa
publicKey := &ecdsa.PublicKey{Curve: reKeys[0].PublicKey.Curve, X: reKeys[0].PublicKey.X, Y: reKeys[0].PublicKey.Y}
if ok := ecdsa.Verify(publicKey, msgHash, new(big.Int).SetBytes(r), new(big.Int).SetBytes(s)); !ok {
t.Errorf("verify failed")
}