code ecdsa sign
在你的代码中,NewP1 和 NewP2 分别代表两个不同的参与方(通常称为 P1 和 P2),它们在生成密钥和签名的过程中各自执行不同的操作。具体来说,NewP1 有三个步骤(Step1、Step2、Step3),而 NewP2 只有两个步骤(Step1、Step2)。
原因分析
-
角色不同:
- P1 在协议中可能扮演一个更为关键的角色,需要执行更多的计算和验证步骤。
- P2 的职责相对简单一些,主要是生成和验证部分数据。
-
数据处理:
- P1 需要处理更多的数据或进行更多的验证步骤,以确保协议的安全性。例如,P1 需要生成和验证更多的证明或承诺。
- P2 的步骤相对简单,主要是生成 Schnorr 证明和计算 Paillier 加密的
(h + xr)/k2。
-
协议设计:
- 协议设计者根据安全性和效率的考虑,可能会将某些步骤分配给特定的参与方。这样可以优化协议的性能或提高安全性。
具体步骤解析
P1 的步骤
Step1
- 生成随机数
k1,计算R1 = k1 * G。 - 生成承诺
cmt并保存cmtD。
func (p1 *P1Context) Step1() (*commitment.Commitment, error) {
// 检查是否在禁止签名列表中,如果是则返回错误
if BanSignList.Has(hex.EncodeToString(p1.publicKey.X.Bytes())) {
return nil, fmt.Errorf("ecdsa sign forbidden, publicKey " + hex.EncodeToString(p1.publicKey.X.Bytes()))
}
// 生成一个随机数 k1,范围在椭圆曲线的阶 (curve.N) 之内
p1.k1 = crypto.RandomNum(curve.N)
// 计算 R1 = k1 * G,其中 G 是椭圆曲线的基点
R1 := curves.ScalarToPoint(curve, p1.k1)
// 基于 sessionID 和 R1 的坐标创建一个新的承诺 (commitment)
cmt := commitment.NewCommitment(p1.sessionID, R1.X, R1.Y)
// 将承诺的信息部分 (witness) 保存到 P1Context 的 cmtD 字段
p1.cmtD = &cmt.Msg
// 返回承诺的承诺部分 (commitment) 和 nil 错误
return &cmt.C, nil
}
Step2
- 验证 P2 的 Schnorr 证明。
- 生成 P1 的 Schnorr 证明并返回。
func (p1 *P1Context) Step2(p2Proof *schnorr.Proof, R2 *curves.ECPoint) (*schnorr.Proof, *commitment.Witness, error) {
// 使用 sessionID 验证 p2 提供的 Schnorr 证明和 R2 点
verify := schnorr.VerifyWithId(p1.sessionID, p2Proof, R2)
if !verify {
// 如果验证失败,返回错误
return nil, nil, fmt.Errorf("schnorr verify fail")
}
// 如果验证成功,将 R2 保存到 P1Context 的 R2 字段
p1.R2 = R2
// 计算 R1 = k1 * G,其中 G 是椭圆曲线的基点
R1 := curves.ScalarToPoint(curve, p1.k1)
// 基于 sessionID 和 k1 生成 Schnorr 证明(proof)
proof, err := schnorr.ProveWithId(p1.sessionID, p1.k1, R1)
if err != nil {
// 如果生成证明失败,返回错误
return nil, nil, err
}
// 返回生成的 Schnorr 证明和之前保存的承诺信息部分(witness)
return proof, p1.cmtD, nil
}
Step3
- 解密
(h + xr)/k2并计算最终签名(r, s)。 - 验证签名并返回。
func (p1 *P1Context) Step3(E_k2_h_xr *big.Int) (*big.Int, *big.Int, error) {
// 获取椭圆曲线的阶 q
q := curve.N
// 计算 Rx = k1 * R2,其中 R2 是 (R2.X, R2.Y)
Rx, _ := curve.ScalarMult(p1.R2.X, p1.R2.Y, p1.k1.Bytes())
// 取 Rx 对 q 取模得到 r
r := new(big.Int).Mod(Rx, q)
// 解密 E_k2_h_xr 得到 k2_h_xr
k2_h_xr, err := p1.paiPriKey.Decrypt(E_k2_h_xr)
if err != nil {
// 如果解密失败,返回错误
return nil, nil, err
}
// 计算 k1 的模逆 k1^-1
k1_1 := new(big.Int).ModInverse(p1.k1, q)
// 计算 s = (k2_h_xr * k1^-1) mod q
s := new(big.Int).Mod(new(big.Int).Mul(k2_h_xr, k1_1), q)
// 计算椭圆曲线阶的一半
halfOrder := new(big.Int).Rsh(q, 1)
// 如果 s 大于椭圆曲线阶的一半,则取 q - s
if s.Cmp(halfOrder) == 1 {
s.Sub(q, s)
}
// 如果 s 为零,返回错误
if s.Sign() == 0 {
return nil, nil, fmt.Errorf("calculated S is zero")
}
// 将 p1.message 从十六进制字符串解码为字节数组
message, err := hex.DecodeString(p1.message)
if err != nil {
// 如果解码失败,返回错误
return nil, nil, err
}
// 使用 ECDSA 验证签名 (r, s)
ok := ecdsa.Verify(p1.publicKey, message, r, s)
if !ok {
// 如果验证失败,将公钥添加到禁止签名列表,并返回错误
BanSignList.Add(hex.EncodeToString(p1.publicKey.X.Bytes()))
return nil, nil, fmt.Errorf("ecdsa sign verify fail")
}
// 返回 r 和 s
return r, s, nil
}
P2 的步骤
Step1
- 生成随机数
k2,计算R2 = k2 * G。 - 生成 Schnorr 证明并返回。
func (p2 *P2Context) Step1(cmtC *commitment.Commitment) (*schnorr.Proof, *curves.ECPoint, error) {
p2.cmtC = cmtC
p2.k2 = crypto.RandomNum(curve.N)
R2 := curves.ScalarToPoint(curve, p2.k2)
proof, err := schnorr.ProveWithId(p2.sessionID, p2.k2, R2)
if err != nil {
return nil, nil, err
}
return proof, R2, nil
}
Step2
- 验证 P1 的 Schnorr 证明。
- 计算 Paillier 加密的
(h + xr)/k2并返回。
func (p2 *P2Context) Step2(cmtD *commitment.Witness, p1Proof *schnorr.Proof) (*big.Int, error) {
q := curve.N
commit := commitment.HashCommitment{}
commit.C = *p2.cmtC
commit.Msg = *cmtD
ok, commitD := commit.Open()
if !ok {
return nil, fmt.Errorf("commitment DeCommit fail")
}
if commitD[0].Cmp(p2.sessionID) != 0 {
return nil, fmt.Errorf("p2 Step2 commitment sessionId error")
}
R1, err := curves.NewECPoint(curve, commitD[1], commitD[2])
if err != nil {
return nil, err
}
verify := schnorr.VerifyWithId(p2.sessionID, p1Proof, R1)
if !verify {
return nil, fmt.Errorf("schnorr verify fail")
}
Rx, _ := curve.ScalarMult(R1.X, R1.Y, p2.k2.Bytes())
r := new(big.Int).Mod(Rx, q)
bytes, err := hex.DecodeString(p2.message)
if err != nil {
return nil, err
}
k2_1 := new(big.Int).ModInverse(p2.k2, q)
h := CalculateM(bytes)
h = new(big.Int).Mul(h, k2_1)
rho := crypto.RandomNum(new(big.Int).Mul(q, q))
rhoq := new(big.Int).Mul(rho, q)
h_rhoq := new(big.Int).Add(h, rhoq)
E_x, err := p2.paiPub.HomoAddPlain(p2.E_x1, p2.x2)
if err != nil {
return nil, err
}
r = new(big.Int).Mul(r, k2_1)
E_xr, err := p2.paiPub.HomoMulPlain(E_x, r)
if err != nil {
return nil, err
}
E_k2_h_xr, err := p2.paiPub.HomoAddPlain(E_xr, h_rhoq)
if err != nil {
return nil, err
}
return E_k2_h_xr, nil
}
总结
NewP1 有三个步骤,而 NewP2 只有两个步骤,主要是因为 P1 在协议中需要执行更多的计算和数据处理。具体原因包括:
- 角色不同:P1 需要进行更多的计算和验证。
- 数据处理:P1 需要处理更多的数据和生成更多的证明。
- 协议设计:为了优化协议的性能和提高安全性,设计者可能将更多的步骤分配给 P1。
通过这种方式,可以确保整个多方计算协议的安全性和效率。