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  }