Skip to main content

Feldman DKG 重新分配

1 份泄漏,三份刷新

P2 遭到泄漏,需要刷新 P2 的份额还是 P1,P2,P3 都刷新?

在门限秘密共享(Threshold Secret Sharing)和 Feldman Verifiable Secret Sharing (VSS) 的背景下,如果某个参与方(例如 P2)的秘密份额遭到泄漏,通常的做法是刷新所有参与方的秘密份额,而不仅仅是刷新 P2 的份额。这样做的原因是为了确保整体系统的安全性和一致性。

原因分析

  1. 整体一致性

    • 如果只刷新 P2 的份额而不刷新其他参与方的份额,新的密钥份额可能无法正确组合成原始的秘密。这是因为每个秘密份额都是基于同一个多项式生成的,单独更改一个份额会破坏这种关系。
  2. 安全性考虑

    • 刷新所有参与方的份额可以防止潜在的攻击者通过已泄漏的份额和未刷新份额之间的关系推导出新的秘密。
    • 如果只刷新 P2 的份额,攻击者仍然可能通过其他未刷新份额的信息进行推测和攻击。

刷新过程

以下是刷新所有参与方份额的过程:

  1. 生成新的多项式

    • 每个参与方生成一个新的随机多项式 gi(x)g_i(x),这些多项式用于更新各自的秘密份额。
  2. 计算新的秘密份额

    • 每个参与方计算新的秘密份额 si=f(i)+gi(i)s_i' = f(i) + g_i(i),其中 f(i)f(i) 是原始多项式在 x=ix = i 处的值,gi(i)g_i(i) 是新的多项式在 x=ix = i 处的值。
  3. 验证和交换新份额

    • 每个参与方通过 Feldman VSS 验证和交换新的秘密份额,确保每个参与方都获得有效的新的秘密份额。
  4. 更新公共密钥

    • 通过 Lagrange 插值法计算新的公共密钥,并验证计算结果是否正确。

总结

在 Feldman VSS 中,如果某个参与方的秘密份额遭到泄漏,通常的做法是刷新所有参与方的秘密份额。这是为了确保整体系统的安全性和一致性。通过生成新的多项式和计算新的秘密份额,所有参与方的秘密份额都会被更新,确保系统的整体秘密保持不变,同时防止潜在的安全威胁。

Reshare3 轮做了什么

在分布式密钥生成(DKG)协议中,每一轮都有特定的任务和步骤。以下是第一轮、第二轮和第三轮的详细说明:

第一轮 (DKGStep1)

任务: 初始化并生成承诺。

1. 检查当前轮次是否为第一轮

if info.RoundNumber != 1 {
return nil, fmt.Errorf("round error")
}

2. 初始化 Feldman VSS

feldman, err := vss.NewFeldman(info.Threshold, info.Total, info.curve)
if err != nil {
return nil, err
}

3. 评估当前设备的秘密份额 ui,生成验证者和份额

verifiers, shares, err := feldman.Evaluate(info.ui)
if err != nil {
return nil, err
}

4. 计算验证者的承诺(不包括链码)

var input []*big.Int
for i := 0; i < len(verifiers); i++ {
input = append(input, verifiers[i].X, verifiers[i].Y)
}
hashCommitment := commitment.NewCommitment(input...)

5. 存储承诺信息和秘密份额

info.deC = &hashCommitment.Msg
info.secretShares = shares
info.verifiers = verifiers

6. 更新当前轮次

info.RoundNumber = 2

7. 创建并广播消息给其他参与者

out := make(map[int]*tss.Message, info.Total-1)
for _, id := range info.Ids() {
if id == info.DeviceNumber {
continue
}
content := tss.KeyStep1Data{C: &hashCommitment.C}
bytes, err := json.Marshal(content)
if err != nil {
return nil, err
}
message := &tss.Message{
From: info.DeviceNumber,
To: id,
Data: string(bytes),
}
out[id] = message
}

第二轮 (DKGStep2)

任务: 验证承诺并生成 Schnorr 证明。

1. 检查当前轮次是否为第二轮

if info.RoundNumber != 2 {
return nil, fmt.Errorf("round error")
}

2. 检查消息数量是否正确

if len(msgs) != (info.Total - 1) {
return nil, fmt.Errorf("messages number error")
}

3. 存储其他参与者的承诺

info.commitmentMap = make(map[int]commitment.Commitment, len(msgs))
for _, msg := range msgs {
if msg.To != info.DeviceNumber {
return nil, fmt.Errorf("message sending error")
}
var content tss.KeyStep1Data
err := json.Unmarshal([]byte(msg.Data), &content)
if err != nil {
return nil, err
}
info.commitmentMap[msg.From] = *content.C
}

4. 生成 Schnorr 证明

uiG := curves.ScalarToPoint(info.curve, info.ui)
proof, err := schnorr.Prove(info.ui, uiG)
if err != nil {
return nil, err
}

5. 更新当前轮次

info.RoundNumber = 3

6. 创建并广播消息给其他参与者

out := make(map[int]*tss.Message, info.Total-1)
for _, id := range info.Ids() {
if id == info.DeviceNumber {
continue
}
content := tss.KeyStep2Data{
Witness: info.deC,
Share: info.secretShares[id-1],
Proof: proof,
}
bytes, err := json.Marshal(content)
if err != nil {
return nil, err
}
message := &tss.Message{
From: info.DeviceNumber,
To: id,
Data: string(bytes),
}
out[id] = message
}

第三轮 (DKGStep3)

任务: 验证秘密份额和 Schnorr 证明,计算新的公共密钥。

1. 检查当前轮次是否为第三轮

if info.RoundNumber != 3 {
return nil, fmt.Errorf("round error")
}

2. 检查消息数量是否正确

if len(msgs) != (info.Total - 1) {
return nil, fmt.Errorf("messages number error")
}

3. 初始化 Feldman VSS

feldman, err := vss.NewFeldman(info.Threshold, info.Total, info.curve)
if err != nil {
return nil, err
}

4. 存储验证者

verifiers := make(map[int][]*curves.ECPoint, len(msgs))
verifiers[info.DeviceNumber] = info.verifiers

5. 获取当前设备的秘密份额

xi := info.secretShares[info.DeviceNumber-1]

6. 验证消息中的承诺和秘密份额

for _, msg := range msgs {
if msg.To != info.DeviceNumber {
return nil, fmt.Errorf("message sending error")
}
var content tss.KeyStep2Data
err := json.Unmarshal([]byte(msg.Data), &content)
if err != nil {
return nil, err
}
hashCommit := commitment.HashCommitment{}
hashCommit.C = info.commitmentMap[msg.From]
hashCommit.Msg = *content.Witness
ok, D := hashCommit.Open()
if !ok {
return nil, fmt.Errorf("commitment DeCommit fail")
}
verifiers[msg.From], err = dkg.UnmarshalVerifiers(info.curve, D, info.Threshold)
if ok, err := feldman.Verify(content.Share, verifiers[msg.From]); !ok {
if err != nil {
return nil, err
} else {
return nil, fmt.Errorf("invalid share for participant")
}
}
xi.Y = new(big.Int).Add(xi.Y, content.Share.Y)
ujPoint := verifiers[msg.From][0]
if ujPoint.X.Cmp(big.NewInt(0)) == 0 || ujPoint.Y.Cmp(big.NewInt(0)) == 0 {
continue
}
point, err := curves.NewECPoint(info.curve, ujPoint.X, ujPoint.Y)
if err != nil {
return nil, err
}
verify := schnorr.Verify(content.Proof, point)
if !verify {
return nil, fmt.Errorf("schnorr verify fail")
}
}

7. 计算新的公共密钥

v := make([]*curves.ECPoint, info.Threshold)
for j := 0; j < info.Threshold; j++ {
v[j] = curves.ScalarToPoint(info.curve, big.NewInt(0))
for _, verifier := range verifiers {
if !verifier[j].IsOnCurve() {
continue
}
v[j], err = v[j].Add(verifier[j])
if err != nil {
return nil, err
}
}
}

8. 计算每个参与方的公共密钥

sharePubKeyMap := make(map[int]*curves.ECPoint, info.Threshold)
for k := 1; k <= info.Total; k++ {
Yi := v[0]
tmp := big.NewInt(1)
for i := 1; i < info.Threshold; i++ {
tmp = tmp.Mul(tmp, big.NewInt(int64(k)))
point := v[i]
point = point.ScalarMult(tmp)
Yi, err = Yi.Add(point)
}
sharePubKeyMap[k] = Yi
}

9. 验证秘密份额的公共密钥是否正确

xiG := curves.ScalarToPoint(info.curve, xi.Y)
if !sharePubKeyMap[info.DeviceNumber].Equals(xiG) {
return nil, fmt.Errorf("public key calculation error")
}

10. 验证新的公共密钥是否与之前的公共密钥相同

if !v[0].Equals(info.publicKey) {
return nil, fmt.Errorf("public key recalculation error")
}

11. 更新信息并返回新的密钥步骤数据

info.shareI = xi.Y
info.publicKey = v[0]
content := &tss.KeyStep3Data{
Id: info.DeviceNumber,
ShareI: info.shareI,
PublicKey: info.publicKey,
SharePubKeyMap: sharePubKeyMap,
}
return content, nil

总结:

  • 第一轮:初始化并生成承诺,广播承诺消息。
  • 第二轮:验证承诺并生成 Schnorr 证明,广播证明消息。
  • 第三轮:验证秘密份额和 Schnorr 证明,计算新的公共密钥,并验证公钥的正确性。

Schnorr 证明 是为了证明什么?

Schnorr 证明是一种零知识证明协议,用于证明某人知道某个秘密值,而不泄露该秘密值本身。在分布式密钥生成(DKG)协议中,Schnorr 证明的主要作用是证明参与者确实知道他们所声称的秘密值(即他们的秘密份额),而不泄露该秘密值。这在分布式系统中是非常重要的,因为它确保了参与者的行为是诚实的,并且他们没有伪造或篡改数据。

具体来说,Schnorr 证明在 DKG 协议中的作用如下:

  1. 证明秘密份额的合法性

    • 每个参与者在第二轮中生成一个 Schnorr 证明,证明他们确实知道他们的秘密份额 ui
    • 其他参与者可以使用这个证明来验证该参与者确实知道 ui,而不需要知道 ui 的实际值。
  2. 防止伪造和篡改

    • 通过验证 Schnorr 证明,参与者可以确保其他参与者没有伪造或篡改他们的秘密份额。
    • 这增加了协议的安全性,因为它防止了恶意参与者试图通过提交虚假的数据来破坏协议。
  3. 零知识属性

    • Schnorr 证明具有零知识属性,这意味着它不会泄露任何关于秘密份额 ui 的信息,除了参与者确实知道 ui 这一事实。
    • 这保证了参与者的隐私,同时仍然允许验证。

Schnorr 证明的基本原理

Schnorr 证明的基本步骤如下:

  1. 生成承诺

    • 选择一个随机数 r,计算 R = r * G,其中 G 是椭圆曲线上的基点。
    • R 作为承诺发送给验证者。
  2. 生成挑战

    • 验证者生成一个挑战 c,通常是通过对承诺 R 和公开信息(例如参与者的公钥)进行哈希计算得到的。
  3. 生成响应

    • 计算响应 s = r + c * x,其中 x 是参与者的秘密份额。
  4. 验证

    • 验证者接受 sR,并验证 s * G = R + c * X,其中 X 是参与者的公钥。
    • 如果验证通过,说明参与者确实知道 x

在 DKG 协议中,Schnorr 证明用于证明每个参与者确实知道他们的秘密份额 ui,从而确保协议的安全性和正确性。