tally
shamir's secret sharing over gf(256).
git clone https://owenrusk.dev/tally.git
tally / tests/test_tally.py -rw-r--r-- · 2728 bytes
1 import itertools
2 import os
3 import unittest
4
5 import tally
6 from tally import ShareError, codec
7
8
9 class SplitJoinTest(unittest.TestCase):
10 def test_round_trip(self) -> None:
11 for length in [1, 2, 7, 31, 100, 1024]:
12 secret = os.urandom(length)
13 lines = tally.split(secret, 3, 5)
14 with self.subTest(length=length):
15 self.assertEqual(tally.join(lines[2:]), secret)
16
17 def test_every_k_subset(self) -> None:
18 secret = os.urandom(40)
19 lines = tally.split(secret, 3, 6)
20 for subset in itertools.combinations(lines, 3):
21 self.assertEqual(tally.join(subset), secret)
22
23 def test_one_id_per_split(self) -> None:
24 lines = tally.split(b"x", 2, 4)
25 self.assertEqual(len({codec.decode(line).id for line in lines}), 1)
26
27 def test_fewer_than_k(self) -> None:
28 lines = tally.split(b"not a real secret", 3, 5)
29 with self.assertRaisesRegex(ShareError, "need 3 shares, got 2"):
30 tally.join(lines[:2])
31
32 def test_same_share_twice(self) -> None:
33 lines = tally.split(b"not a real secret", 2, 3)
34 with self.assertRaisesRegex(ShareError, "shares 1 and 2 are both share 1"):
35 tally.join([lines[0], lines[0]])
36
37 def test_different_splits(self) -> None:
38 a = tally.split(b"not a real secret", 2, 3)
39 b = tally.split(b"not a real secret", 2, 3)
40 with self.assertRaisesRegex(ShareError, "different splits"):
41 tally.join([a[0], b[1]])
42
43 def test_typo_names_the_share(self) -> None:
44 lines = tally.split(b"not a real secret", 2, 3)
45 typo = lines[1][:-1] + ("0" if lines[1][-1] != "0" else "2")
46 with self.assertRaisesRegex(ShareError, "^share 2: "):
47 tally.join([lines[0], typo])
48
49 def test_more_than_k(self) -> None:
50 secret = os.urandom(24)
51 lines = tally.split(secret, 3, 7)
52 self.assertEqual(tally.join(lines), secret)
53 self.assertEqual(tally.join(reversed(lines)), secret)
54
55 def test_damaged_extra_share(self) -> None:
56 # a share that's wrong but still passes its own checksum
57 lines = tally.split(b"not a real secret", 3, 5)
58 share = codec.decode(lines[4])
59 flipped = bytes([share.data[0] ^ 1]) + share.data[1:]
60 bad = codec.encode(codec.Share(share.id, share.k, share.x, flipped))
61 with self.assertRaisesRegex(ShareError, "share 4 doesn't agree with the first 3"):
62 tally.join([*lines[:3], bad])
63
64 def test_nothing(self) -> None:
65 with self.assertRaises(ShareError):
66 tally.split(b"", 2, 3)
67 with self.assertRaises(ShareError):
68 tally.join([])
69
70
71 if __name__ == "__main__":
72 unittest.main()