From ce2b670511b1f526b29b549f38c8eceaed2c777d Mon Sep 17 00:00:00 2001 From: John-Mark Gurney Date: Tue, 30 May 2023 01:31:25 -0700 Subject: [PATCH] add/improve docs, more exhaustive testing of combinations. --- shamirss.py | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/shamirss.py b/shamirss.py index a6e1901..076b681 100644 --- a/shamirss.py +++ b/shamirss.py @@ -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 (, ).''' + will be a tuple of (, ).''' 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):