init: v1.0.0

This commit is contained in:
yaole
2026-05-27 23:03:00 +08:00
commit 8d97f750eb
466 changed files with 80067 additions and 0 deletions
+42
View File
@@ -0,0 +1,42 @@
package ssss
/*
package ssss implements Shamir's Secret Sharing Scheme
Usage:
func TestSSSS() {
secret := make([]byte, 16)
// (3,5)门限
shares, err := Split(secret, 3, 5, 0, rand.Reader)
if err != nil {
//
}
// shares 是5份分片数据
// 收集至少3份分片数据
recoveryShares := shares[:3]
//
recoveredSecret, err := Restore(recoveryShares)
if err != nil {
//
}
if bytes.Compare(secret, recoveredSecret) != 0 {
fmt.Println("recoveredSecret and secret are different")
}
}
每个分片数据格式定义为:
SharedSlice ::= SEQUENCE {
Version INTEGER DEFAULT 0,-- default 0
Threshold INTEGER, -- 门限值
Length INTEGER, -- 原秘密值字节数
Degree INTEGER, -- 拉格朗日多项式系数域在F_2上的扩域次数
X INTEGER, -- x值
Y INTEGER, -- y=f(x)
Token [0]IMPLICIT OCTET STRING OPTINAL --随机字节,同一secret的n个分片应该一致
}
*/
+23
View File
@@ -0,0 +1,23 @@
package ssss
import (
"xdx.jelly/xgcl/gerrors"
)
//go:generate stringer -type=ErrorCode -linecomment -output=errors_string.go errors.go
type ErrorCode gerrors.ErrorCode
func (e ErrorCode) Error() string {
return gerrors.Format(uint32(e), e.String())
}
// error codes
const (
ErrInvalidInput ErrorCode = 0x0100c000 + iota //输入不合法
ErrRestoreFailed //恢复秘密值失败
ErrDivideZero //除数为0
ErrSharesMayBeTheSame //输入相同分片
ErrNeedMoreShares //分片数量不足
ErrSecretTooLarge //秘密值太大
ErrBadShares //分片错误
)
+30
View File
@@ -0,0 +1,30 @@
// Code generated by "stringer -type=ErrorCode -linecomment -output=errors_string.go errors.go"; DO NOT EDIT.
package ssss
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[ErrInvalidInput-16826368]
_ = x[ErrRestoreFailed-16826369]
_ = x[ErrDivideZero-16826370]
_ = x[ErrSharesMayBeTheSame-16826371]
_ = x[ErrNeedMoreShares-16826372]
_ = x[ErrSecretTooLarge-16826373]
_ = x[ErrBadShares-16826374]
}
const _ErrorCode_name = "输入不合法恢复秘密值失败除数为0输入相同分片分片数量不足秘密值太大分片错误"
var _ErrorCode_index = [...]uint8{0, 15, 36, 46, 64, 82, 97, 109}
func (i ErrorCode) String() string {
i -= 16826368
if i >= ErrorCode(len(_ErrorCode_index)-1) {
return "ErrorCode(" + strconv.FormatInt(int64(i+16826368), 10) + ")"
}
return _ErrorCode_name[_ErrorCode_index[i]:_ErrorCode_index[i+1]]
}
+291
View File
@@ -0,0 +1,291 @@
package ssss
import (
"crypto/rand"
"io"
"math/big"
"xdx.jelly/xgcl/gerrors"
)
/* coefficients of some irreducible polynomials over GF(2) */
// var irred_coeff = [...]byte{
// 4, 3, 1, 5, 3, 1, 4, 3, 1, 7, 3, 2, 5, 4, 3, 5, 3, 2, 7, 4, 2, 4, 3, 1, 10, 9, 3, 9, 4, 2, 7, 6, 2, 10, 9,
// 6, 4, 3, 1, 5, 4, 3, 4, 3, 1, 7, 2, 1, 5, 3, 2, 7, 4, 2, 6, 3, 2, 5, 3, 2, 15, 3, 2, 11, 3, 2, 9, 8, 7, 7,
// 2, 1, 5, 3, 2, 9, 3, 1, 7, 3, 1, 9, 8, 3, 9, 4, 2, 8, 5, 3, 15, 14, 10, 10, 5, 2, 9, 6, 2, 9, 3, 2, 9, 5,
// 2, 11, 10, 1, 7, 3, 2, 11, 2, 1, 9, 7, 4, 4, 3, 1, 8, 3, 1, 7, 4, 1, 7, 2, 1, 13, 11, 6, 5, 3, 2, 7, 3, 2,
// 8, 7, 5, 12, 3, 2, 13, 10, 6, 5, 3, 2, 5, 3, 2, 9, 5, 2, 9, 7, 2, 13, 4, 3, 4, 3, 1, 11, 6, 4, 18, 9, 6,
// 19, 18, 13, 11, 3, 2, 15, 9, 6, 4, 3, 1, 16, 5, 2, 15, 14, 6, 8, 5, 2, 15, 11, 2, 11, 6, 2, 7, 5, 3, 8,
// 3, 1, 19, 16, 9, 11, 9, 6, 15, 7, 6, 13, 4, 3, 14, 13, 3, 13, 6, 3, 9, 5, 2, 19, 13, 6, 19, 10, 3, 11,
// 6, 5, 9, 2, 1, 14, 3, 2, 13, 3, 1, 7, 5, 4, 11, 9, 8, 11, 6, 5, 23, 16, 9, 19, 14, 6, 23, 10, 2, 8, 3,
// 2, 5, 4, 3, 9, 6, 4, 4, 3, 2, 13, 8, 6, 13, 11, 1, 13, 10, 3, 11, 6, 5, 19, 17, 4, 15, 14, 7, 13, 9, 6,
// 9, 7, 3, 9, 7, 1, 14, 3, 2, 11, 8, 2, 11, 6, 4, 13, 5, 2, 11, 5, 1, 11, 4, 1, 19, 10, 3, 21, 10, 6, 13,
// 3, 1, 15, 7, 5, 19, 18, 10, 7, 5, 3, 12, 7, 2, 7, 5, 1, 14, 9, 6, 10, 3, 2, 15, 13, 12, 12, 11, 9, 16,
// 9, 7, 12, 9, 3, 9, 5, 2, 17, 10, 6, 24, 9, 3, 17, 15, 13, 5, 4, 3, 19, 17, 8, 15, 6, 3, 19, 6, 1}
// irreducible polynomials of GF(2)
var irred_coeff = [...][3]byte{
{4, 3, 1}, // x^8 + x^4 + x^3 + x + 1
{5, 3, 1}, // x^16 + x^5 + x^3 + x + 1
{4, 3, 1}, // x^24 + x^4 + x^3 + x + 1
{7, 3, 2}, // x^32 + x^7 + x^3 + x^2 + 1
{5, 4, 3}, // x^40 + x^5 + x^4 + x^3 + 1
{5, 3, 2}, // x^48 + x^5 + x^3 + x^2 + 1
{7, 4, 2}, // x^56 + x^7 + x^4 + x^2 + 1
{4, 3, 1}, // x^64 + x^4 + x^3 + x + 1
{10, 9, 3}, // x^72 + x^10 + x^9 + x^3 + 1
{9, 4, 2}, // x^80 + x^9 + x^4 + x^2 + 1
{7, 6, 2}, // x^88 + x^7 + x^6 + x^2 + 1
{10, 9, 6}, // x^96 + x^10 + x^9 + x^6 + 1
{4, 3, 1}, // x^104 + x^4 + x^3 + x + 1
{5, 4, 3}, // x^112 + x^5 + x^4 + x^3 + 1
{4, 3, 1}, // x^120 + x^4 + x^3 + x + 1
{7, 2, 1}, // x^128 + x^7 + x^2 + x + 1
{5, 3, 2}, // x^136 + x^5 + x^3 + x^2 + 1
{7, 4, 2}, // x^144 + x^7 + x^4 + x^2 + 1
{6, 3, 2}, // x^152 + x^6 + x^3 + x^2 + 1
{5, 3, 2}, // x^160 + x^5 + x^3 + x^2 + 1
{15, 3, 2}, // x^168 + x^15 + x^3 + x^2 + 1
{11, 3, 2}, // x^176 + x^11 + x^3 + x^2 + 1
{9, 8, 7}, // x^184 + x^9 + x^8 + x^7 + 1
{7, 2, 1}, // x^192 + x^7 + x^2 + x + 1
{5, 3, 2}, // x^200 + x^5 + x^3 + x^2 + 1
{9, 3, 1}, // x^208 + x^9 + x^3 + x + 1
{7, 3, 1}, // x^216 + x^7 + x^3 + x + 1
{9, 8, 3}, // x^224 + x^9 + x^8 + x^3 + 1
{9, 4, 2}, // x^232 + x^9 + x^4 + x^2 + 1
{8, 5, 3}, // x^240 + x^8 + x^5 + x^3 + 1
{15, 14, 10}, // x^248 + x^15 + x^14 + x^10 + 1
{10, 5, 2}, // x^256 + x^10 + x^5 + x^2 + 1
{9, 6, 2}, // x^264 + x^9 + x^6 + x^2 + 1
{9, 3, 2}, // x^272 + x^9 + x^3 + x^2 + 1
{9, 5, 2}, // x^280 + x^9 + x^5 + x^2 + 1
{11, 10, 1}, // x^288 + x^11 + x^10 + x + 1
{7, 3, 2}, // x^296 + x^7 + x^3 + x^2 + 1
{11, 2, 1}, // x^304 + x^11 + x^2 + x + 1
{9, 7, 4}, // x^312 + x^9 + x^7 + x^4 + 1
{4, 3, 1}, // x^320 + x^4 + x^3 + x + 1
{8, 3, 1}, // x^328 + x^8 + x^3 + x + 1
{7, 4, 1}, // x^336 + x^7 + x^4 + x + 1
{7, 2, 1}, // x^344 + x^7 + x^2 + x + 1
{13, 11, 6}, // x^352 + x^13 + x^11 + x^6 + 1
{5, 3, 2}, // x^360 + x^5 + x^3 + x^2 + 1
{7, 3, 2}, // x^368 + x^7 + x^3 + x^2 + 1
{8, 7, 5}, // x^376 + x^8 + x^7 + x^5 + 1
{12, 3, 2}, // x^384 + x^12 + x^3 + x^2 + 1
{13, 10, 6}, // x^392 + x^13 + x^10 + x^6 + 1
{5, 3, 2}, // x^400 + x^5 + x^3 + x^2 + 1
{5, 3, 2}, // x^408 + x^5 + x^3 + x^2 + 1
{9, 5, 2}, // x^416 + x^9 + x^5 + x^2 + 1
{9, 7, 2}, // x^424 + x^9 + x^7 + x^2 + 1
{13, 4, 3}, // x^432 + x^13 + x^4 + x^3 + 1
{4, 3, 1}, // x^440 + x^4 + x^3 + x + 1
{11, 6, 4}, // x^448 + x^11 + x^6 + x^4 + 1
{18, 9, 6}, // x^456 + x^18 + x^9 + x^6 + 1
{19, 18, 13}, // x^464 + x^19 + x^18 + x^13 + 1
{11, 3, 2}, // x^472 + x^11 + x^3 + x^2 + 1
{15, 9, 6}, // x^480 + x^15 + x^9 + x^6 + 1
{4, 3, 1}, // x^488 + x^4 + x^3 + x + 1
{16, 5, 2}, // x^496 + x^16 + x^5 + x^2 + 1
{15, 14, 6}, // x^504 + x^15 + x^14 + x^6 + 1
{8, 5, 2}, // x^512 + x^8 + x^5 + x^2 + 1
{15, 11, 2}, // x^520 + x^15 + x^11 + x^2 + 1
{11, 6, 2}, // x^528 + x^11 + x^6 + x^2 + 1
{7, 5, 3}, // x^536 + x^7 + x^5 + x^3 + 1
{8, 3, 1}, // x^544 + x^8 + x^3 + x + 1
{19, 16, 9}, // x^552 + x^19 + x^16 + x^9 + 1
{11, 9, 6}, // x^560 + x^11 + x^9 + x^6 + 1
{15, 7, 6}, // x^568 + x^15 + x^7 + x^6 + 1
{13, 4, 3}, // x^576 + x^13 + x^4 + x^3 + 1
{14, 13, 3}, // x^584 + x^14 + x^13 + x^3 + 1
{13, 6, 3}, // x^592 + x^13 + x^6 + x^3 + 1
{9, 5, 2}, // x^600 + x^9 + x^5 + x^2 + 1
{19, 13, 6}, // x^608 + x^19 + x^13 + x^6 + 1
{19, 10, 3}, // x^616 + x^19 + x^10 + x^3 + 1
{11, 6, 5}, // x^624 + x^11 + x^6 + x^5 + 1
{9, 2, 1}, // x^632 + x^9 + x^2 + x + 1
{14, 3, 2}, // x^640 + x^14 + x^3 + x^2 + 1
{13, 3, 1}, // x^648 + x^13 + x^3 + x + 1
{7, 5, 4}, // x^656 + x^7 + x^5 + x^4 + 1
{11, 9, 8}, // x^664 + x^11 + x^9 + x^8 + 1
{11, 6, 5}, // x^672 + x^11 + x^6 + x^5 + 1
{23, 16, 9}, // x^680 + x^23 + x^16 + x^9 + 1
{19, 14, 6}, // x^688 + x^19 + x^14 + x^6 + 1
{23, 10, 2}, // x^696 + x^23 + x^10 + x^2 + 1
{8, 3, 2}, // x^704 + x^8 + x^3 + x^2 + 1
{5, 4, 3}, // x^712 + x^5 + x^4 + x^3 + 1
{9, 6, 4}, // x^720 + x^9 + x^6 + x^4 + 1
{4, 3, 2}, // x^728 + x^4 + x^3 + x^2 + 1
{13, 8, 6}, // x^736 + x^13 + x^8 + x^6 + 1
{13, 11, 1}, // x^744 + x^13 + x^11 + x + 1
{13, 10, 3}, // x^752 + x^13 + x^10 + x^3 + 1
{11, 6, 5}, // x^760 + x^11 + x^6 + x^5 + 1
{19, 17, 4}, // x^768 + x^19 + x^17 + x^4 + 1
{15, 14, 7}, // x^776 + x^15 + x^14 + x^7 + 1
{13, 9, 6}, // x^784 + x^13 + x^9 + x^6 + 1
{9, 7, 3}, // x^792 + x^9 + x^7 + x^3 + 1
{9, 7, 1}, // x^800 + x^9 + x^7 + x + 1
{14, 3, 2}, // x^808 + x^14 + x^3 + x^2 + 1
{11, 8, 2}, // x^816 + x^11 + x^8 + x^2 + 1
{11, 6, 4}, // x^824 + x^11 + x^6 + x^4 + 1
{13, 5, 2}, // x^832 + x^13 + x^5 + x^2 + 1
{11, 5, 1}, // x^840 + x^11 + x^5 + x + 1
{11, 4, 1}, // x^848 + x^11 + x^4 + x + 1
{19, 10, 3}, // x^856 + x^19 + x^10 + x^3 + 1
{21, 10, 6}, // x^864 + x^21 + x^10 + x^6 + 1
{13, 3, 1}, // x^872 + x^13 + x^3 + x + 1
{15, 7, 5}, // x^880 + x^15 + x^7 + x^5 + 1
{19, 18, 10}, // x^888 + x^19 + x^18 + x^10 + 1
{7, 5, 3}, // x^896 + x^7 + x^5 + x^3 + 1
{12, 7, 2}, // x^904 + x^12 + x^7 + x^2 + 1
{7, 5, 1}, // x^912 + x^7 + x^5 + x + 1
{14, 9, 6}, // x^920 + x^14 + x^9 + x^6 + 1
{10, 3, 2}, // x^928 + x^10 + x^3 + x^2 + 1
{15, 13, 12}, // x^936 + x^15 + x^13 + x^12 + 1
{12, 11, 9}, // x^944 + x^12 + x^11 + x^9 + 1
{16, 9, 7}, // x^952 + x^16 + x^9 + x^7 + 1
{12, 9, 3}, // x^960 + x^12 + x^9 + x^3 + 1
{9, 5, 2}, // x^968 + x^9 + x^5 + x^2 + 1
{17, 10, 6}, // x^976 + x^17 + x^10 + x^6 + 1
{24, 9, 3}, // x^984 + x^24 + x^9 + x^3 + 1
{17, 15, 13}, // x^992 + x^17 + x^15 + x^13 + 1
{5, 4, 3}, // x^1000 + x^5 + x^4 + x^3 + 1
{19, 17, 8}, // x^1008 + x^19 + x^17 + x^8 + 1
{15, 6, 3}, // x^1016 + x^15 + x^6 + x^3 + 1
{19, 6, 1}, // x^1024 + x^19 + x^6 + x + 1
}
// f2m, the extention filed over GF(2) of degree m
type gf2x struct {
poly *big.Int
degree int
intPool []*big.Int
}
func init() {
}
// newGF2x return the irred. polynomial of degree deg, where deg must divided by 8.
func newGF2x(deg int) *gf2x {
poly := big.NewInt(1)
poly.SetBit(poly, deg, 1)
// poly.SetBit(poly, int(irred_coeff[3*(deg/8-1)+0]), 1)
// poly.SetBit(poly, int(irred_coeff[3*(deg/8-1)+1]), 1)
// poly.SetBit(poly, int(irred_coeff[3*(deg/8-1)+2]), 1)
poly.SetBit(poly, int(irred_coeff[deg/8-1][0]), 1)
poly.SetBit(poly, int(irred_coeff[deg/8-1][1]), 1)
poly.SetBit(poly, int(irred_coeff[deg/8-1][2]), 1)
return &gf2x{
poly: poly,
degree: deg,
}
}
func (g *gf2x) getInt() (r *big.Int) {
if len(g.intPool) > 0 {
r = g.intPool[len(g.intPool)-1]
r.SetInt64(0)
g.intPool = g.intPool[:len(g.intPool)-1]
return
}
return new(big.Int)
}
func (g *gf2x) putInt(r *big.Int) {
g.intPool = append(g.intPool, r)
}
func (g *gf2x) rand(r io.Reader) (*big.Int, error) {
for {
x, err := rand.Int(r, g.poly)
x.SetBit(x, g.degree, 0)
if err == nil && x.Sign() != 0 {
return x, nil
}
if err != nil {
return nil, gerrors.WithStack(err)
}
}
}
func (g *gf2x) add(z, x, y *big.Int) {
z.Xor(x, y)
}
// z can't be y, z could be x
func (g *gf2x) mul(z, x, y *big.Int) {
var yy *big.Int
if z == y {
yy = g.getInt().Set(y)
defer g.putInt(yy)
} else {
yy = y
}
b := g.getInt()
defer g.putInt(b)
// note that z may be x, so make b a copy of x
b.Set(x)
if yy.Bit(0) == 1 {
// y = 1 + y1*X + y2*X^2 + ...
z.Set(b)
} else {
z.SetInt64(0)
}
for i := 1; i < g.degree; i++ {
b.Lsh(b, 1)
if b.Bit(g.degree) == 1 {
b.Xor(b, g.poly)
}
if yy.Bit(i) == 1 {
z.Xor(z, b)
}
}
}
var one = new(big.Int).SetInt64(1)
// extend eudlic algorithm
func (f *gf2x) invert(z, x *big.Int) error {
u := f.getInt()
v := f.getInt()
g := f.getInt()
h := f.getInt()
t := f.getInt()
defer func() {
f.putInt(u)
f.putInt(v)
f.putInt(g)
f.putInt(h)
f.putInt(t)
}()
if x.Sign() == 0 {
return gerrors.WithAnnotating(ErrDivideZero, "input x is zero")
}
u.Set(x)
v.Set(f.poly)
g.SetInt64(0)
h.SetInt64(1)
// x*h = u mod poly
// x*g = v mod poly
for u.Cmp(one) != 0 {
i := u.BitLen() - v.BitLen()
if i < 0 {
u, v, h, g = v, u, g, h
i = -i
}
t.Lsh(v, uint(i))
u.Xor(u, t)
t.Lsh(g, uint(i))
h.Xor(h, t)
}
z.Set(h)
return nil
}
+285
View File
@@ -0,0 +1,285 @@
package ssss
import (
"bytes"
"encoding/asn1"
"io"
"math/big"
"xdx.jelly/xgcl/gerrors"
"xdx.jelly/xgcl/gmath"
)
const MaxSecretLength = 1024 / 8
// SharedSlice 秘密分片数据结构
//
// SharedSlice ::= SEQUENCE {
// Version INTEGER
// Threshold INTEGER
// Length INTEGER
// Degree INTEGER
// X INTEGER
// Y INTEGER
// Token [0]IMPLICIT OCTET STRING OPTINAL
// }
type SharedSlice struct {
Version int `asn1:"default:0"`
Threshold int // 门限
Length int // 原秘密消息长度
Degree int
X int // 分片x值
Y *big.Int // 分片f(x)值
Token []byte `asn1:"optional,implicit,tag:0"` // Optional 随机值,一个secret的n个分享值相同。
}
// Sharing 秘密分享
// secret 待分拆的秘密值,不超过MaxSecretLength字节(64),返回nShares个分享值。
// threshold, nShares, (t,n)门限值,例如(3,5)
// securityParam, 安全参数,取值128256512或1024. 取0则自动适配。如16字节secret对应128.
// rand, 随机数Reader
//
// 注: securityParam取值应不小于len(secret)*8, 若输入的securityParam < len(secret)*8,
// 则securityParam自动适配为 (len(secret) + 7)/8)*64
// 例: secret为16字节的SM4密钥,(3,5)门限分割
//
// shares, err := Split(secret, 3,5,0,rand.Reader)
// shares为5个share([]byte). 每个share为SharedSlice的ASN.1编码。
func Split(secret []byte, threshold, nShares int, securityParam int, rand io.Reader) ([][]byte, error) {
if len(secret) > MaxSecretLength {
return nil, gerrors.WithAnnotatingf(ErrSecretTooLarge, "secret is limits to %d bytes", MaxSecretLength)
}
if securityParam < len(secret)*8 {
securityParam = len(secret) * 8
}
switch {
case securityParam <= 128:
securityParam = 128
case securityParam <= 256:
securityParam = 256
case securityParam <= 512:
securityParam = 512
default:
securityParam = 1024
}
f := newGF2x(securityParam)
iSecret := new(big.Int).SetBytes(secret)
iShares, err := split(rand, threshold, nShares, iSecret, f)
if err != nil {
return nil, err
}
shares := make([][]byte, 0, nShares)
token := make([]byte, 16)
n, err := rand.Read(token)
if n < len(token) || err != nil {
token = nil
}
for k, v := range iShares {
slice := SharedSlice{
Version: 0,
Threshold: threshold,
Length: len(secret),
Degree: securityParam,
X: k,
Y: v,
Token: token,
}
b, err := asn1.Marshal(slice)
if err != nil {
return nil, gerrors.WithStack(err)
}
shares = append(shares, b)
}
return shares, nil
}
// Restore 秘密恢复,输入threshold个秘密分享值,返回 Secret.
// tShares 包含至少t个分拆值。否则返回错误。
func Restore(tShares [][]byte) ([]byte, error) {
iShares := make(map[int]*big.Int)
t := -1
length := -1
var token []byte
securityParam := -1
for _, s := range tShares {
slice := new(SharedSlice)
_, err := asn1.Unmarshal(s, slice)
if err != nil {
return nil, gerrors.ChainErrors(ErrInvalidInput, err)
}
if t == -1 {
t = slice.Threshold
} else if t != slice.Threshold {
return nil, gerrors.WithAnnotating(ErrBadShares, "share slices are not compatible")
}
if length == -1 {
length = slice.Length
} else if length != slice.Length {
return nil, gerrors.WithAnnotating(ErrBadShares, "share slices are not compatible")
}
if token == nil {
token = slice.Token
} else if !bytes.Equal(token, slice.Token) {
return nil, gerrors.WithAnnotating(ErrBadShares, "share slices are not compatible")
}
if securityParam == -1 {
securityParam = slice.Degree
} else if securityParam != slice.Degree {
return nil, gerrors.WithAnnotating(ErrBadShares, "share slices are not compatible")
}
if securityParam < slice.Y.BitLen() {
return nil, gerrors.WithAnnotating(ErrBadShares, "Share slices are broken")
}
iShares[slice.X] = slice.Y
}
f := newGF2x(securityParam)
iSecret, err := restore(t, iShares, f)
if err != nil {
return nil, gerrors.ChainErrors(ErrRestoreFailed, err)
}
secret := make([]byte, length)
if length*8 < iSecret.BitLen() {
return nil, gerrors.WithAnnotating(ErrBadShares, "Share slices are broken")
}
if err = gmath.FillBytes(iSecret, secret); err != nil {
return nil, gerrors.WithAnnotating(ErrBadShares, "Share slices are broken")
}
return secret, nil
}
// eval y = f(x), where f(X) = \sigma coeff[i]*X^i + X^t in field f
func horner(y, x *big.Int, coeff []*big.Int, f *gf2x) {
y.Set(x)
for i := len(coeff) - 1; i > 0; i-- {
f.add(y, y, coeff[i])
f.mul(y, y, x)
}
f.add(y, y, coeff[0])
}
// return F(1), F(2),...F(n), satisfy deg(F) = t - 1, F(0) = m
// m is the secret, deg(m) < f.degree
func split(rand io.Reader, t, n int, secret *big.Int, f *gf2x) (map[int]*big.Int, error) {
coeff := make([]*big.Int, t)
coeff[0] = secret
for i := 1; i < t; i++ {
if b, err := f.rand(rand); err != nil {
return nil, gerrors.WithStack(err)
} else {
coeff[i] = b
}
}
x := new(big.Int)
y := new(big.Int)
shares := make(map[int]*big.Int)
for i := 1; i <= n; i++ {
x.SetInt64(int64(i))
horner(y, x, coeff, f)
shares[i] = new(big.Int).Set(y)
}
return shares, nil
}
func restore(t int, shares map[int]*big.Int, f *gf2x) (*big.Int, error) {
if len(shares) < t {
return nil, ErrNeedMoreShares
}
A := make([][]*big.Int, 0, t)
y := make([]*big.Int, 0, t)
for i := 0; i < t; i++ {
row := make([]*big.Int, 0, t)
for j := 0; j < t; j++ {
row = append(row, new(big.Int))
}
A = append(A, row)
y = append(y, new(big.Int))
}
X := new(big.Int)
i := 0
for x, fx := range shares {
X.SetInt64(int64(x))
A[t-1][i].SetInt64(1)
for j := t - 2; j >= 0; j-- {
f.mul(A[j][i], A[j+1][i], X)
}
y[i].Set(fx)
f.mul(X, X, A[0][i])
f.add(y[i], y[i], X)
i++
if i >= t {
break
}
}
/*
Now A =
1^{t-1} 2^{t-1} ... t^{t-1}
...........
1^2 2^2 ... t^2
1 2 ... t
1 1 ... 1
and we have
(y[0],..., y[t-1]) = (a[0], ..., a[t-1]) * A.
All need to do is to solve a[0], which is the secret.
*/
if err := restore_secret(A, y, f); err != nil {
return nil, err
}
return y[t-1], nil
}
func restore_secret(A [][]*big.Int, y []*big.Int, f *gf2x) error {
n := len(A)
h := new(big.Int)
for i := 0; i < n; i++ {
var j int
if A[i][i].Sign() == 0 {
found := false
for j = i + 1; j < n; j++ {
if A[i][j].Sign() != 0 {
found = true
break
}
}
if !found {
return ErrSharesMayBeTheSame
}
for k := i; k < n; k++ {
A[k][i], A[k][j] = A[k][j], A[k][i]
}
y[i], y[j] = y[j], y[i]
}
for j = i + 1; j < n; j++ {
if A[i][j].Sign() != 0 {
for k := i + 1; k < n; k++ {
f.mul(h, A[k][i], A[i][j])
f.mul(A[k][j], A[k][j], A[i][i])
f.add(A[k][j], A[k][j], h)
}
f.mul(h, y[i], A[i][j])
f.mul(y[j], y[j], A[i][i])
f.add(y[j], y[j], h)
}
}
}
if A[n-1][n-1].Sign() == 0 {
return ErrSharesMayBeTheSame
}
if err := f.invert(h, A[n-1][n-1]); err != nil {
return gerrors.ChainErrors(ErrBadShares, err)
}
f.mul(y[n-1], y[n-1], h)
return nil
}
+215
View File
@@ -0,0 +1,215 @@
package ssss
import (
"bytes"
"crypto/rand"
"encoding/asn1"
"fmt"
"io"
"math/big"
"testing"
"xdx.jelly/xgcl/gmath"
)
func TestSharingASN1(t *testing.T) {
threshold := 3
n := 5
// 16字节b需要拆分
b := make([]byte, 16)
_, _ = io.ReadFull(rand.Reader, b)
// Shares是n个秘密分片
shares, err := Split(b, threshold, n, 0, rand.Reader)
if err != nil {
t.Fatal(err)
}
// recoverdShares 凑齐threshold个秘密分片
recoverdShares := make([][]byte, 0)
i := 0
for _, s := range shares {
recoverdShares = append(recoverdShares, s)
i++
if i == threshold {
break
}
}
// recovered是恢复的秘密值
recovered, err := Restore(recoverdShares)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(b, recovered) {
t.Fatal("recoverd and secret are different")
}
}
func TestSharingASN1Fuzz(t *testing.T) {
b := make([]byte, 16)
for k := 0; k < 1000; k++ {
for n := 3; n < 8; n++ {
for threshold := 1; threshold <= n; threshold++ {
// 16字节b需要拆分
_, _ = io.ReadFull(rand.Reader, b)
shares, err := Split(b, threshold, n, 0, rand.Reader)
if err != nil {
t.Fatal(err)
}
recoverdShares := make([][]byte, 0)
i := 0
for _, s := range shares {
recoverdShares = append(recoverdShares, s)
i++
if i == threshold {
break
}
}
recovered, err := Restore(recoverdShares)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(b, recovered) {
t.Fatal("recoverd and secret are different")
}
}
}
}
}
func TestFieldMul(t *testing.T) {
f := newGF2x(8)
var x *big.Int
y := new(big.Int)
one := new(big.Int).SetInt64(1)
for k := 0; k < 10000; k++ {
x, _ = f.rand(rand.Reader)
y.Set(one)
for i := 0; i < 1<<8-1; i++ {
f.mul(y, y, x)
}
if y.Cmp(one) != 0 {
t.Log("error x:", x.Text(16))
t.Fatal("mul error")
}
}
}
func TestFieldInvert(t *testing.T) {
f := newGF2x(8)
z := new(big.Int)
y := new(big.Int)
one := new(big.Int).SetInt64(1)
for k := 0; k < 10000; k++ {
x, _ := f.rand(rand.Reader)
if f.invert(z, x) != nil {
t.Fatal()
}
f.mul(y, x, z)
if y.Cmp(one) != 0 {
t.Fatal("invert error")
}
}
}
func TestHorner(t *testing.T) {
//f(x) = x^2 + x + 2
f := newGF2x(64)
coeff := []*big.Int{
new(big.Int).SetInt64(2),
new(big.Int).SetInt64(1),
}
x := new(big.Int).SetInt64(3)
y := new(big.Int)
horner(y, x, coeff, f)
if y.Cmp(gmath.BigInt4) != 0 {
t.Fatal()
}
}
func TestSharing(t *testing.T) {
b := make([]byte, 16)
f := newGF2x(128)
for k := 0; k < 1000; k++ {
// 16字节b需要拆分
_, _ = io.ReadFull(rand.Reader, b)
// secret, _ := f.rand(rand.Reader)
secret := new(big.Int).SetBytes(b)
for n := 3; n < 8; n++ {
for threshold := 1; threshold <= n; threshold++ {
shares, err := split(rand.Reader, threshold, n, secret, f)
if err != nil {
t.Fatal(err)
}
recovered, err := restore(threshold, shares, f)
if err != nil || recovered.Cmp(secret) != 0 {
t.Log("restore secret:", recovered.Text(16))
t.Log("secret :", secret.Text(16))
t.Fatal(err)
}
}
}
}
}
func TestSigle(t *testing.T) {
f := newGF2x(64)
secret, _ := f.rand(rand.Reader)
threshold := 1
n := 5
shares, err := split(rand.Reader, threshold, n, secret, f)
if err != nil {
t.Fatal(err)
}
for k, v := range shares {
t.Logf("%d-%s\n", k, v.Text(16))
}
var v0 big.Int
for k, v := range shares {
v0.SetInt64(int64(k))
f.add(&v0, &v0, v)
if v0.Cmp(secret) != 0 {
t.Fatal()
}
}
recovered, err := restore(threshold, shares, f)
if err != nil || recovered.Cmp(secret) != 0 {
t.Log("restore secret:", recovered.Text(16))
t.Log("secret :", secret.Text(16))
t.Fatal(err)
}
}
func TestASN1(t *testing.T) {
slice := SharedSlice{
Version: 0,
Threshold: 3,
Length: 16,
X: 1,
Y: new(big.Int).SetUint64(16),
Token: make([]byte, 16),
}
b, err := asn1.Marshal(slice)
if err != nil {
t.Fatal(err)
}
fmt.Printf("%x\n", b)
slice2 := new(SharedSlice)
_, err = asn1.Unmarshal(b, slice2)
fmt.Println(err, slice2)
}