笔者不是很懂门限签名的原理,如果想要了解关于门限签名的原理,请搜索其它资源
门限签名简介:
在( k , n ) \left( k,n\right)(k,n)门限签名模型中,所有节点都持有一个公共的 p u b l i c k e y publickeypublickey ,每一个节点 i ii 都有各自的私钥 p r i v a t e k e y i privatekey_{i}privatekeyi.
每一个副本 i ii 会用自己的 p r i v a t e k e y i privatekey_{i}privatekeyi 对一个信息(m)贡献部分签名
ρ i ← tsign i ( m ) \rho_{i} \leftarrow \operatorname{tsign}_{i}(m)ρi←tsigni(m).
对于一个部分签名集合 { ρ i } i ∈ I \left\{\rho_{i}\right\}_{i \in I}{ρi}i∈I, 当∣ i ∣ = k |i|=k∣i∣=k时,就可以生成一个数字签名
σ ← tcombine ( m , { ρ i } i ∈ I ) \sigma \leftarrow \text { tcombine }\left(m,\left\{\rho_{i}\right\}_{i \in I}\right)σ← tcombine (m,{ρi}i∈I)
其它节点可以用公共的 p u b l i c k e y publickeypublickey来验证数字签名是否正确
t v e r i f y ( m , σ , p u b l i c k e y ) tverify(m, \sigma, publickey)tverify(m,σ,publickey)
使用Go语言第三方库来实现门限签名
在一个拜占庭系统中, n = 3 f + 1 n=3f+1n=3f+1, 那么门限签名的阈值一般定义为 k = 2 f + 1 k=2f+1k=2f+1, 那么我们的门限签名模型为( k = 2 f + 1 , n = 3 f + 1 ) (k=2f+1, n=3f+1)(k=2f+1,n=3f+1).
Go语言中的第三方库kyber-tbls有关于门限签名的实现与测试,不过其测试代码中假设所有节点都不是拜占庭节点,本文将给出含有拜占庭节点的测试代码.在测试时,主要考虑两种情况,一种是节点掉线没有响应,一种是节点能正确的算出其部分签名,但在传输时恶意的将自己部分签名进行篡改后再发送给其它节点.
- 节点掉线
为了方便,定义一个结构体代表节点
type server struct {
privateKey *share.PriShare //节点的私钥
publicKey *share.PubPoly //publicKey对于每个节点都一样
message []byte
}
节点的初始化与部分签名
func makeServer(n, f, threshold int, suite *bn256.Suite) ([]server, [][]byte) {
m := "hello"
servers := make([]server, n)
secret := suite.G1().Scalar().Pick(suite.RandomStream())
priPoly := share.NewPriPoly(suite.G2(), threshold, secret, suite.RandomStream())
pubPoly := priPoly.Commit(suite.G2().Point().Base())
sigShares := make([][]byte, 0)
for i, x := range priPoly.Shares(n) {
servers[i].privateKey = x //将私钥赋予节点i
servers[i].publicKey = pubPoly //将公钥赋予节点i
servers[i].message = []byte(m)
sig, _ := tbls.Sign(suite, servers[i].privateKey, servers[i].message) //生成部分签名
if i == f {
continue //假设拜占庭节点为f=1,不会做出响应
} else {
sigShares = append(sigShares, sig) //生成数字签名
}
}
return servers, sigShares
}
验证签名
func threshldVerify(servers []server, n, f, threshold int, suite *bn256.Suite, sigShares [][]byte, t *testing.T) []string {
results := make([]string, n)
for i := 0; i < n; i++ {
sig, _ := tbls.Recover(suite, servers[i].publicKey, servers[i].message, sigShares, threshold, n)
err := bls.Verify(suite, servers[i].publicKey.Commit(), servers[i].message, sig) //用公钥验证数字签名
if err == nil {
results[i] = "Success"
} else {
results[i] = "Fault"
}
}
return results
}
- 节点发送篡改后的签名
这一部分只需要将makeServer
函数更改部分代码即可,拜占庭节点会篡改自己生成的部分签名
for i, x := range priPoly.Shares(n) {
servers[i].privateKey = x //将私钥赋予节点i
servers[i].publicKey = pubPoly
servers[i].message = []byte(m) //将公钥赋予节点i
sig, _ := tbls.Sign(suite, servers[i].privateKey, servers[i].message) //生成部分签名
if i == f {
sig[0] ^= 0x10 //篡改自己生成的部分签名
} else {
sigShares = append(sigShares, sig) //生成数字签名
}
}
return servers, sigShares
完整代码
package thresholdtest
import (
"testing"
"go.dedis.ch/kyber/v3/pairing/bn256"
"go.dedis.ch/kyber/v3/share"
"go.dedis.ch/kyber/v3/sign/bls"
"go.dedis.ch/kyber/v3/sign/tbls"
)
type server struct {
privateKey *share.PriShare
publicKey *share.PubPoly
message []byte
}
func makeServer(n, f, threshold int, suite *bn256.Suite) ([]server, [][]byte) {
m := "hello"
servers := make([]server, n)
secret := suite.G1().Scalar().Pick(suite.RandomStream())
priPoly := share.NewPriPoly(suite.G2(), threshold, secret, suite.RandomStream())
pubPoly := priPoly.Commit(suite.G2().Point().Base())
sigShares := make([][]byte, 0)
for i, x := range priPoly.Shares(n) {
servers[i].privateKey = x //将私钥赋予节点i
servers[i].publicKey = pubPoly //将公钥赋予节点i
servers[i].message = []byte(m)
sig, _ := tbls.Sign(suite, servers[i].privateKey, servers[i].message) //生成部分签名
if i == f {
sig[0] ^= 0x10
} else {
sigShares = append(sigShares, sig) //生成数字签名
}
}
return servers, sigShares
}
func threshldVerify(servers []server, n, f, threshold int, suite *bn256.Suite, sigShares [][]byte, t *testing.T) []string {
results := make([]string, n)
for i := 0; i < n; i++ {
sig, _ := tbls.Recover(suite, servers[i].publicKey, servers[i].message, sigShares, threshold, n)
err := bls.Verify(suite, servers[i].publicKey.Commit(), servers[i].message, sig) //用公钥验证数字签名
if err == nil {
results[i] = "Success"
} else {
results[i] = "Fault"
}
}
return results
}
func TestWithOneFailer(t *testing.T) {
suite := bn256.NewSuite()
n := 4
f := 1
threshold := 2*f + 1
servers, sigShares := makeServer(n, f, threshold, suite)
results := threshldVerify(servers, n, f, threshold, suite, sigShares, t)
for i := range results {
if results[i] != "Success" {
t.Errorf("server [%d] recover fault.\n", i)
}
}
}