github.com/Psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/sss/sss.go (about) 1 // Package sss implements Shamir's Secret Sharing algorithm over GF(2^8). 2 // 3 // Shamir's Secret Sharing algorithm allows you to securely share a secret with 4 // N people, allowing the recovery of that secret if K of those people combine 5 // their shares. 6 // 7 // It begins by encoding a secret as a number (e.g., 42), and generating N 8 // random polynomial equations of degree K-1 which have an X-intercept equal to 9 // the secret. Given K=3, the following equations might be generated: 10 // 11 // f1(x) = 78x^2 + 19x + 42 12 // f2(x) = 128x^2 + 171x + 42 13 // f3(x) = 121x^2 + 3x + 42 14 // f4(x) = 91x^2 + 95x + 42 15 // etc. 16 // 17 // These polynomials are then evaluated for values of X > 0: 18 // 19 // f1(1) = 139 20 // f2(2) = 896 21 // f3(3) = 1140 22 // f4(4) = 1783 23 // etc. 24 // 25 // These (x, y) pairs are the shares given to the parties. In order to combine 26 // shares to recover the secret, these (x, y) pairs are used as the input points 27 // for Lagrange interpolation, which produces a polynomial which matches the 28 // given points. This polynomial can be evaluated for f(0), producing the secret 29 // value--the common x-intercept for all the generated polynomials. 30 // 31 // If fewer than K shares are combined, the interpolated polynomial will be 32 // wrong, and the result of f(0) will not be the secret. 33 // 34 // This package constructs polynomials over the field GF(2^8) for each byte of 35 // the secret, allowing for fast splitting and combining of anything which can 36 // be encoded as bytes. 37 // 38 // This package has not been audited by cryptography or security professionals. 39 package sss 40 41 import ( 42 "crypto/rand" 43 "errors" 44 "io" 45 ) 46 47 var ( 48 // ErrInvalidCount is returned when the count parameter is invalid. 49 ErrInvalidCount = errors.New("N must be >= K") 50 // ErrInvalidThreshold is returned when the threshold parameter is invalid. 51 ErrInvalidThreshold = errors.New("K must be > 1") 52 ) 53 54 // Split the given secret into N shares of which K are required to recover the 55 // secret. Returns a map of share IDs (1-255) to shares. 56 func Split(n, k byte, secret []byte) (map[byte][]byte, error) { 57 return split(n, k, secret, rand.Reader) 58 } 59 60 // SplitUsingReader splits the given secret, as Split does, but using the 61 // specified reader to create random polynomials. Use for deterministic 62 // splitting; caller must ensure reader is cryptographically secure. 63 func SplitUsingReader( 64 n, k byte, secret []byte, reader io.Reader) (map[byte][]byte, error) { 65 66 return split(n, k, secret, reader) 67 } 68 69 func split(n, k byte, secret []byte, randReader io.Reader) (map[byte][]byte, error) { 70 if k <= 1 { 71 return nil, ErrInvalidThreshold 72 } 73 74 if n < k { 75 return nil, ErrInvalidCount 76 } 77 78 shares := make(map[byte][]byte, n) 79 80 for _, b := range secret { 81 p, err := generate(k-1, b, randReader) 82 if err != nil { 83 return nil, err 84 } 85 86 for x := byte(1); x <= n; x++ { 87 shares[x] = append(shares[x], eval(p, x)) 88 } 89 } 90 91 return shares, nil 92 } 93 94 // Combine the given shares into the original secret. 95 // 96 // N.B.: There is no way to know whether the returned value is, in fact, the 97 // original secret. 98 func Combine(shares map[byte][]byte) []byte { 99 var secret []byte 100 for _, v := range shares { 101 secret = make([]byte, len(v)) 102 break 103 } 104 105 points := make([]pair, len(shares)) 106 for i := range secret { 107 p := 0 108 for k, v := range shares { 109 points[p] = pair{x: k, y: v[i]} 110 p++ 111 } 112 secret[i] = interpolate(points, 0) 113 } 114 115 return secret 116 }