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, 安全参数,取值128,256,512或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 }