p1-MPC钱包01
0x-wen

探索MPC钱包:数字资产安全的革命性创新

随着数字货币的普及,资产安全成为用户关注的焦点。传统的钱包依赖于单一私钥,存在单点故障的风险。MPC钱包的出现,以其独特的多方计算技术,为数字资产的安全存储和交易提供了新的解决方案。

MPC钱包的核心原理

MPC钱包的工作原理基于一个简单的概念:将私钥分割成多个碎片,每个碎片由不同的参与者持有。只有当足够数量的参与者共同参与计算时,才能恢复出完整的私钥,进而授权交易。

实现MPC钱包的关键技术

  1. 私钥分片:使用特定的算法,如Shamir’s Secret Sharing,将私钥分割成多个份额,每个份额本身不包含任何有关原始私钥的信息。
  2. 阈值签名:定义一个阈值,只有当达到或超过这个阈值数量的份额共同参与时,才能生成有效的签名。
  3. 多方计算协议:确保在不泄露各自份额的前提下,多个参与者可以协同计算出私钥的完整形式。

MPC钱包的优势

  1. 提高安全性:由于私钥的分散存储,即使部分份额被泄露,也不会导致资产损失。
  2. 降低用户门槛:用户无需管理复杂的私钥或助记词,简化了使用流程。
  3. 适应性强:MPC钱包适用于需要高度安全性的场景,如大额资产存储和金融交易。

okx开源库

参考链接:okx-threshold-lib,
提供了基于阈值签名方案的多方计算(MPC)实现。这个库是用 Go 语言编写的,支持 ECDSA 和 Ed25519 签名算法的阈值签名。

产品逻辑

  1. 结合okx-MPC钱包,总体将私钥分为三方: 用户设备、用户云盘、交易所,其中任意两方即可完成消息签名
  2. 紧急情况下可以通过用户云盘将私钥导出

算法实现

  1. ECDSA 阈值签名:使用 Feldman’s VSS(Verifiable Secret Sharing,可验证秘密共享)算法生成密钥份额,并使用 Lindell 17 协议实现两方签名。
  2. Ed25519 阈值签名:为 Ed25519 签名方案实现阈值签名功能。
  3. Bip32 密钥派生:支持密钥份额的非硬化派生,链码(chaincode)由多方共同生成。
  4. 密钥份额刷新:当一方的密钥份额丢失或有新的参与者加入时,支持密钥份额的刷新。

实现步骤

  1. 初始化:设置阈值参数和参与方数量。
  2. 密钥生成:使用秘密共享算法生成私钥的多个份额,并分配给不同的参与方。
  3. 密钥份额刷新:在密钥份额丢失或有新参与方加入时,重新分配或生成新的密钥份额,并更新现有份额。
  4. 聚合签名:在达到预设的阈值数量后,聚合各参与方的中间值,生成最终的签名。
  5. 签名验证:使用相应的公钥验证生成的签名是否有效。

代码实践

  1. 初始化:设置加密类型、阈值参数和参与方数量。

    1
    2
    3
    // curve 决定采用哪种椭圆曲线来生成对应密钥
    setUp1 := dkg.NewSetUp(1, 3, curve)

  2. 密钥生成:使用秘密共享算法生成私钥的多个份额,并分配给不同的参与方。

    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
    }
  3. 密钥份额刷新:在密钥份额丢失或有新参与方加入时,重新分配或生成新的密钥份额,并更新现有份额。

    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
    func (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
    }
  4. 聚合签名:在达到预设的阈值数量后,聚合各参与方的中间值,生成最终的签名。

    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
    }
  5. 签名验证:使用相应的公钥验证生成的签名是否有效。

    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")
    }
由 Hexo 驱动 & 主题 Keep
总字数 41.7k