|
|
@@ -26,7 +26,27 @@ |
|
|
|
# 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 itertools |
|
|
|
import operator |
|
|
|
import secrets |
|
|
|
import unittest.mock |
|
|
@@ -63,10 +83,11 @@ def create_shares(data, k, nshares): |
|
|
|
'''Given data, create nshares, such that given any k shares, |
|
|
|
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 |
|
|
|
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) |
|
|
|
|
|
|
@@ -83,9 +104,13 @@ def create_shares(data, k, nshares): |
|
|
|
range(1, nshares + 1) ] |
|
|
|
|
|
|
|
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: |
|
|
|
raise ValueError('not enough shares to recover') |
|
|
@@ -234,7 +259,14 @@ class TestShamirSS(unittest.TestCase): |
|
|
|
# that one share isn't enough |
|
|
|
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): |
|
|
|
|
|
|
|