| @@ -26,7 +26,27 @@ | |||||
| # ls shamirss.py | entr sh -c ' date; python -m coverage run -m unittest shamirss && coverage report -m' | # ls shamirss.py | entr sh -c ' date; python -m coverage run -m unittest shamirss && coverage report -m' | ||||
| # | # | ||||
| ''' | |||||
| An implementation of Shamir's Secret Sharing. | |||||
| This is over GF(2^256), so unlike some other implementations that are | |||||
| over primes, it is valid for ALL values, and the output will be exactly | |||||
| the same size as the secret. This also limits the number of shared to | |||||
| 255. | |||||
| Sample usage: | |||||
| ``` | |||||
| import random | |||||
| from shamirss import * | |||||
| data = random.SystemRandom().randbytes(32) | |||||
| shares = create_shares(data, 3, 5) | |||||
| rdata = recover_data([ shares[1], shares[2], shares[4] ], 3) | |||||
| print(rdata == data) | |||||
| ``` | |||||
| ''' | |||||
| import functools | import functools | ||||
| import itertools | |||||
| import operator | import operator | ||||
| import secrets | import secrets | ||||
| import unittest.mock | import unittest.mock | ||||
| @@ -63,10 +83,11 @@ def create_shares(data, k, nshares): | |||||
| '''Given data, create nshares, such that given any k shares, | '''Given data, create nshares, such that given any k shares, | ||||
| data can be recovered. | data can be recovered. | ||||
| data must be bytes, or able to be converted to bytes. | |||||
| data must be bytes, or able to be converted to bytes, e.g. a list | |||||
| of ints in the range [ 0, 255 ]. | |||||
| The return value will be a list of length nshares. Each element | The return value will be a list of length nshares. Each element | ||||
| will be a tuple of (<int in range [1, nshares + 1)>, <bytes>).''' | |||||
| will be a tuple of (<int in range [ 1, nshares ]>, <bytes>).''' | |||||
| data = bytes(data) | data = bytes(data) | ||||
| @@ -83,9 +104,13 @@ def create_shares(data, k, nshares): | |||||
| range(1, nshares + 1) ] | range(1, nshares + 1) ] | ||||
| def recover_data(shares, k): | def recover_data(shares, k): | ||||
| '''Recover the value given shares, where k is needed. | |||||
| '''Recover the value given shares, where k is the number of | |||||
| shares needed. | |||||
| shares must be as least length of k. | |||||
| shares must be as least length of k.''' | |||||
| Each element of shares is from one returned by create_shares, | |||||
| that is a tuple of an int and bytes.''' | |||||
| if len(shares) < k: | if len(shares) < k: | ||||
| raise ValueError('not enough shares to recover') | raise ValueError('not enough shares to recover') | ||||
| @@ -234,7 +259,14 @@ class TestShamirSS(unittest.TestCase): | |||||
| # that one share isn't enough | # that one share isn't enough | ||||
| self.assertRaises(ValueError, recover_data, [ a[0] ], 2) | self.assertRaises(ValueError, recover_data, [ a[0] ], 2) | ||||
| self.assertEqual(val, recover_data(a[:2], 2)) | |||||
| for i, j in itertools.combinations(range(3), 2): | |||||
| self.assertEqual(val, recover_data([ a[i], a[j] ], 2)) | |||||
| self.assertEqual(val, recover_data([ a[j], a[i] ], 2)) | |||||
| a = create_shares(val, 15, 30) | |||||
| for i in range(5): | |||||
| self.assertEqual(val, recover_data([ a[j] for j in random.sample(range(30), 15) ], 15)) | |||||
| def test_gf2p8_inv(self): | def test_gf2p8_inv(self): | ||||