tally
shamir's secret sharing over gf(256).
git clone https://owenrusk.dev/tally.git
tally / tests/test_shamir.py -rw-r--r-- · 2911 bytes
1 import itertools 2 import os 3 import random 4 import unittest 5 from collections import Counter 6 7 from tally import shamir 8 9 LENGTHS = [1, 2, 3, 15, 16, 17, 64, 255, 256, 1000] 10 SHAPES = [(2, 2), (2, 3), (3, 5), (5, 5), (4, 7), (6, 9)] 11 12 13 class ShamirTest(unittest.TestCase): 14 def test_round_trip(self) -> None: 15 secret = b"not a real secret" 16 shares = shamir.split(secret, 3, 5) 17 self.assertEqual(len(shares), 5) 18 self.assertEqual(shamir.combine(shares[:3]), secret) 19 20 def test_many_lengths(self) -> None: 21 for length in LENGTHS: 22 secret = os.urandom(length) 23 shares = shamir.split(secret, 3, 5) 24 self.assertTrue(all(len(data) == length for _, data in shares)) 25 with self.subTest(length=length): 26 self.assertEqual(shamir.combine(random.sample(shares, 3)), secret) 27 28 def test_every_k_subset_rebuilds(self) -> None: 29 for k, n in SHAPES: 30 secret = os.urandom(random.choice([1, 17, 100])) 31 shares = shamir.split(secret, k, n) 32 for subset in itertools.combinations(shares, k): 33 with self.subTest(k=k, n=n, xs=[x for x, _ in subset]): 34 self.assertEqual(shamir.combine(list(subset)), secret) 35 36 def test_order_doesnt_matter(self) -> None: 37 secret = os.urandom(32) 38 shares = shamir.split(secret, 4, 6) 39 for _ in range(20): 40 self.assertEqual(shamir.combine(random.sample(shares, 4)), secret) 41 42 def test_widest(self) -> None: 43 secret = os.urandom(3) 44 shares = shamir.split(secret, 2, 255) 45 self.assertEqual(shamir.combine([shares[0], shares[254]]), secret) 46 shares = shamir.split(secret, 255, 255) 47 self.assertEqual(shamir.combine(shares), secret) 48 49 def test_fewer_than_k_is_not_enough(self) -> None: 50 for k, n in SHAPES: 51 secret = os.urandom(32) 52 shares = shamir.split(secret, k, n) 53 with self.subTest(k=k, n=n): 54 self.assertNotEqual(shamir.combine(shares[: k - 1]), secret) 55 56 def test_one_share_of_two_looks_like_noise(self) -> None: 57 # every byte of the secret is 0. a single share should still be all over the place 58 secret = bytes(6000) 59 for x, data in shamir.split(secret, 2, 3): 60 counts = Counter(data) 61 with self.subTest(x=x): 62 self.assertEqual(len(counts), 256) 63 self.assertLess(max(counts.values()), 3 * len(secret) // 256) 64 65 def test_bad_k_and_n(self) -> None: 66 for k, n in [(1, 3), (4, 3), (2, 256), (0, 0)]: 67 with self.assertRaises(ValueError): 68 shamir.split(b"x", k, n) 69 70 def test_same_x_twice(self) -> None: 71 shares = shamir.split(b"x", 2, 3) 72 with self.assertRaises(ValueError): 73 shamir.combine([shares[0], shares[0]]) 74 75 76 if __name__ == "__main__": 77 unittest.main()