ed25519 step1,2,3
code
func sign_p1_p3(p1Data, p3Data *tss.KeyStep3Data, publicKey *edwards.PublicKey, message []byte) {
fmt.Println("=========sign_p1_p3========")
partList := []int{1, 3}
p1 := NewEd25519Sign(1, 2, partList, p1Data.ShareI, publicKey, hex.EncodeToString(message))
p3 := NewEd25519Sign(3, 2, partList, p3Data.ShareI, publicKey, hex.EncodeToString(message))
p1Step1, _ := p1.SignStep1()
p3Step1, _ := p3.SignStep1()
p1Step2, _ := p1.SignStep2([]*tss.Message{p3Step1[1]})
p3Step2, _ := p3.SignStep2([]*tss.Message{p1Step1[3]})
si_1, r, _ := p1.SignStep3([]*tss.Message{p3Step2[1]})
fmt.Println("si_1: ", si_1, r)
si_3, r, _ := p3.SignStep3([]*tss.Message{p1Step2[3]})
fmt.Println("si_3: ", si_3, r)
s := new(big.Int).Add(si_1, si_3)
// outputs the signature
signature := edwards.NewSignature(r, s)
ret := signature.Verify(message, publicKey)
fmt.Println("Verify: ", ret)
}
在门限签名(Threshold Signature)协议中,每个参与者都需要执行所有的步骤(step1, step2, step3)以确保整个签名过程的安全性和正确性。以下是每个步骤的详细解释,以及为什么每个参与者都需要执行这些步骤:
Step 1: 生成并广播承诺(Commitment)
每个参与者生成一个随机数ki并计算其相应的点Ri,然后生成并广播一个承诺(commitment)。这一过程确保了每个参与者的随机数ki在后续步骤中不会被篡改。
Step 2: 接收并验证承诺,生成零知识证明(zk-SNARK)
每个参与者接收其他参与者的承诺,并验证这些承诺的正确性。同时,每个参与者生成自己的零知识证明,证明其ki的正确性。这个步骤确保了所有参与者的ki都是有效的,并且没有被篡改。
Step 3: 计算签名
每个参与者接收并验证其他参与者的零知识证明,计算最终的签名si和R。这个步骤是门限签名协议的核心,确保了所有参与者共同生成一个有效的签名。
具体到你的代码
在你的代码中,P1 和 P3 都执行了 step1, step2, step3,这是因为:
- Step 1:P1 和 P3 各自生成并广播自己的承诺。
- Step 2:P1 和 P3 接收并验证对方的承诺,并生成自己的零知识证明。
- Step 3:P1 和 P3 接收并验证对方的零知识证明,计算最终的签名。
通过这种方式,P1 和 P3 共同生成了一个有效的签名。每个参与者都执行所有步骤的原因是为了确保整个过程的正确性和安全性。每个步骤都是必不可少的,缺少任何一个步骤都会导致签名过程的失败或不安全。
流程图
-
P1 和 P3 执行 Step 1:
- P1 生成
k1并计算R1,生成承诺并广播。 - P3 生成
k3并计算R3,生成承诺并广播。
- P1 生成
-
P1 和 P3 执行 Step 2:
- P1 接收并验证 P3 的承诺,生成零知识证明并广播。
- P3 接收并验证 P1 的承诺,生成零知识证明并广播。
-
P1 和 P3 执行 Step 3:
- P1 接收并验证 P3 的零知识证明,计算
si_1和R。 - P3 接收并验证 P1 的零知识证明,计算
si_3和R。
- P1 接收并验证 P3 的零知识证明,计算
最终,P1 和 P3 各自计算出部分签名si_1和si_3,并将它们相加得到完整的签名。
为什么每个参与者都要执行所有步骤?
- 安全性:每个步骤都有其独特的安全性保障,确保没有参与者能够欺骗或篡改签名过程。
- 正确性:每个步骤都确保所有参与者的输入和输出都是正确的,最终生成的签名是有效的。
因此,在门限签名协议中,每个参与者都需要执行所有步骤,以确保整个签名过程的安全性和正确性。
顺序无关
在门限签名(Threshold Signature)协议中,si_1和si_3等部分签名的顺序通常是没有严格要求的,因为最终的签名是通过将这些部分签名相加得到的。然而,确保所有部分签名被正确收集和处理仍然是至关重要的。以下是一些详细的解释和注意事项:
数学原理
门限签名协议的核心思想是通过将多个参与者的部分签名相加,生成一个完整的签名。假设有两个参与者 P1 和 P3,各自生成部分签名si_1和si_3,最终的签名S是通过相加这些部分签名得到的:
S = si_1 + si_3
由于加法的交换性和结合性,si_1和si_3的顺序不影响最终的结果:
si_1 + si_3 = si_3 + si_1
实际应用中的注意事项
尽管数学上顺序无关紧要,但在实际应用中,仍需注意以下几点:
- 数据完整性:确保所有部分签名都被正确收集和处理。任何一个部分签名的丢失或错误都会导致最终签名的失败。
- 数据验证:在收集部分签名时,可以对每个部分签名进行验证,确保其有效性。这可以通过零知识证明等方法实现。
- 并发处理:如果部分签名是并发生成和传输的,需要确保在最终汇总时所有部分签名都已到达。
示例代码
以下是一个示例代码,展示如何汇总部分签名并生成最终签名:
package main
import (
"crypto/ed25519"
"crypto/rand"
"fmt"
"math/big"
)
func main() {
// 假设已经生成 了ed25519公钥和私钥对
publicKey, privateKey, _ := ed25519.GenerateKey(rand.Reader)
// 假设消息是"Hello, world!"
message := []byte("Hello, world!")
// P1和P3的部分签名
si_1 := new(big.Int).SetBytes(ed25519.Sign(privateKey, message))
si_3 := new(big.Int).SetBytes(ed25519.Sign(privateKey, message))
// 汇总部分签名
finalSignature := new(big.Int).Add(si_1, si_3)
// 生成最终签名
signature := append(finalSignature.Bytes(), publicKey...)
// 验证签名
valid := ed25519.Verify(publicKey, message, signature[:ed25519.SignatureSize])
fmt.Println("Signature valid:", valid)
}
总结
- 顺序无关:
si_1和si_3的顺序在数学上无关紧要,因为加法具有交换性。 - 数据完整性和验证:确保所有部分签名都被正确收集和验证。
- 并发处理:在并发环境下,确保所有部分签名都已到达并被正确处理。
通过以上方法,可以确保在实际应用中正确汇总部分签名并生成最终签名。
签名片段收集方式
在正式的应用中,如何将各个参与者生成的部分签名(如si_1和si_3)汇集到一个客户端上,取决于具体的应用场景和安全需求。以下是几种常见的方法:
1. 网络接口调用(API)
各个参与者通过网络接口(API)将部分签名发送到一个集中处理的服务器或客户端。这种方法适合于网络环境下的应用,具有较高的自动化程度。
优点:
- 自动化程度高,适合大规模应用。
- 可以实现实时性和高效性。
缺点:
- 需要处理网络安全问题,如数据传输加密和身份验证。
示例:
// 假设有一个API接口用于接收部分签名
func submitPartialSignature(partialSignature *big.Int, participantID int) error {
// 将部分签名提交到服务器
// 例如通过HTTP POST请求
// ...
return nil
}
// P1和P3生成部分签名后,调用API接口提交
submitPartialSignature(si_1, 1)
submitPartialSignature(si_3, 3)
2. 扫码(QR Code)
各个参与者生成部分签名后,通过生成二维码(QR Code)展示出来,另一个客户端可以扫码获取部分签名。这种方法适合于物理环境下的应用,如多方签名钱包。
优点:
- 安全性较高,适合于物理隔离的环境。
- 适合于没有网络连接的场景。
缺点:
- 自动化程度低,依赖于人工操作。
- 可能不适合大规模应用。
示例:
// 生成QR Code展示部分签名
func generateQRCode(data string) error {
// 使用QR Code库生成二维码
// ...
return nil
}
// P1和P3生成部分签名后,生成二维码展示
generateQRCode(si_1.String())
generateQRCode(si_3.String())
3. 离线文件传输
各个参与者生成部分签名后,通过离线文件(如 USB 驱动器、SD 卡)将部分签名传输到一个集中处理的客户端。这种方法适合于高度安全的环境,如不允许网络连接的场景。
优点:
- 安全性最高,适合于高度敏感的环境。
- 不依赖于网络连接。
缺点:
- 自动化程度低,依赖于人工操作。
- 传输速度较慢,可能不适合实时应用。
示例:
// 将部分签名保存到文件
func saveToFile(partialSignature *big.Int, filename string) error {
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
_, err = file.WriteString(partialSignature.String())
return err
}
// P1和P3生成部分签名后,保存到文件
saveToFile(si_1, "si_1.txt")
saveToFile(si_3, "si_3.txt")
选择方法的考虑因素
- 安全性:确保部分签名在传输过程中不会被篡改或窃取。
- 实时性:根据应用需求选择是否需要实时传输部分签名。
- 自动化程度:考虑应用的规模和操作的便利性。
- 环境约束:根据具体应用环境(如是否有网络连接)选择合适的方法。
结论
在实际应用中,选择适合的方法将各个参与者生成的部分签名汇集到一个客户端上,需要综合考虑安全性、实时性、自动化程度和环境约束等因素。对于高安全性要求的应用,可能需要结合多种方法以确保安全和可靠性。
每个参与者之间计算一遍
func sign_p1_p3(p1Data, p3Data *tss.KeyStep3Data, publicKey *edwards.PublicKey, message []byte) {
fmt.Println("=========sign_p1_p3========")
partList := []int{1, 3}
p1 := NewEd25519Sign(1, 2, partList, p1Data.ShareI, publicKey, hex.EncodeToString(message))
p3 := NewEd25519Sign(3, 2, partList, p3Data.ShareI, publicKey, hex.EncodeToString(message))
p1Step1, _ := p1.SignStep1()
p3Step1, _ := p3.SignStep1()
p1Step2, _ := p1.SignStep2([]*tss.Message{p3Step1[1]})
p3Step2, _ := p3.SignStep2([]*tss.Message{p1Step1[3]})
si_1, r, _ := p1.SignStep3([]*tss.Message{p3Step2[1]})
fmt.Println("si_1: ", si_1, r)
si_3, r, _ := p3.SignStep3([]*tss.Message{p1Step2[3]})
fmt.Println("si_3: ", si_3, r)
s := new(big.Int).Add(si_1, si_3)
// outputs the signature
signature := edwards.NewSignature(r, s)
ret := signature.Verify(message, publicKey)
fmt.Println("Verify: ", ret)
}
看这个测试用例中P1和P3都执行了 step1, step2,step3,为什么都要执行一遍?
在门限签名(Threshold Signature)协议中,每个参与者都需要执行所有的步骤(step1, step2, step3)以确保整个签名过程的安全性和正确性。以下是每个步骤的详细解释,以及为什么每个参与者都需要执行这些步骤:
Step 1: 生成并广播承诺(Commitment)
每个参与者生成一个随机数ki并计算其相应的点Ri,然后生成并广播一个承诺(commitment)。这一过程确保了每个参与者的随机数ki在后续步骤中不会被篡改。
Step 2: 接收并验证承诺,生成零知识证明(zk-SNARK)
每个参与者接收其他参与者的承诺,并验证这些承诺的正确性。同时,每个参与者生成自己的零知识证明,证明其ki的正确性。这个步骤确保了所有参与者的ki都是有效的,并且没有被篡改。
Step 3: 计算签名
每个参与者接收并验证其他参与者的零知识证明,计算最终的签名si和R。这个步骤是门限签名协议的核心,确保了所有参与者共同生成一个有效的签名。
具体到你的代码
在你的代码中,P1 和 P3 都执行了 step1, step2, step3,这是因为:
- Step 1:P1 和 P3 各自生成并广播自己的承诺。
- Step 2:P1 和 P3 接收并验证对方的承诺,并生成自己的零知识证明。
- Step 3:P1 和 P3 接收并验证对方的零知识证明,计算最终的签名。
通过这种方式,P1 和 P3 共同生成了一个有效的签名。每个参与者都执行所有步骤的原因是为了确保整个过程的正确性和安全性。每个步骤都是必不可少的,缺少任何一个步骤都会导致签名过程的失败或不安全。
流程图
-
P1 和 P3 执行 Step 1:
- P1 生成
k1并计算R1,生成承诺并广播。 - P3 生成
k3并计算R3,生成承诺并广播。
- P1 生成
-
P1 和 P3 执行 Step 2:
- P1 接收并验证 P3 的承诺,生成零知识证明并广播。
- P3 接收并验证 P1 的承诺,生成零知识证明并广播。
-
P1 和 P3 执行 Step 3:
- P1 接收并验证 P3 的零知识证明,计算
si_1和R。 - P3 接收并验证 P1 的零知识证明,计算
si_3和R。
- P1 接收并验证 P3 的零知识证明,计算
最终,P1 和 P3 各自计算出部分签名si_1和si_3,并将它们相加得到完整的签名。