tally
shamir's secret sharing over gf(256).
git clone https://owenrusk.dev/tally.git
tally / tests/test_codec.py -rw-r--r-- · 2917 bytes
1 import os
2 import unittest
3
4 from tally import codec
5 from tally.codec import Share, ShareError
6
7 SHARE = Share(id=bytes.fromhex("a1b2c3d4"), k=3, x=2, data=b"not a real secret")
8
9
10 class CodecTest(unittest.TestCase):
11 def test_round_trip(self) -> None:
12 self.assertEqual(codec.decode(codec.encode(SHARE)), SHARE)
13
14 def test_round_trip_lengths(self) -> None:
15 for length in range(1, 40):
16 share = Share(codec.new_id(), 2, 255, os.urandom(length))
17 self.assertEqual(codec.decode(codec.encode(share)), share)
18
19 def test_looks_right(self) -> None:
20 text = codec.encode(SHARE)
21 self.assertTrue(text.startswith("t1-"))
22 groups = text.split("-")[1:]
23 self.assertTrue(all(len(g) == codec.GROUP for g in groups[:-1]))
24 self.assertTrue(set("".join(groups)) <= set(codec.ALPHABET))
25
26 def test_case_and_spacing_dont_matter(self) -> None:
27 text = codec.encode(SHARE)
28 self.assertEqual(codec.decode(text.upper()), SHARE)
29 self.assertEqual(codec.decode(" " + text.replace("-", " - ") + "\n"), SHARE)
30
31 def test_lookalikes(self) -> None:
32 text = codec.encode(SHARE)
33 body = text[3:]
34 self.assertTrue("0" in body and "1" in body)
35 mistyped = text[:3] + body.replace("0", "o").replace("1", "l")
36 self.assertEqual(codec.decode(mistyped), SHARE)
37 mistyped = text[:3] + body.replace("1", "i")
38 self.assertEqual(codec.decode(mistyped), SHARE)
39
40 def test_not_a_share(self) -> None:
41 for text in ["", "hello", "t1", "x1-abcd", "t1-ab", "t1-abcd-u000"]:
42 with self.subTest(text=text), self.assertRaises(ShareError):
43 codec.decode(text)
44
45 def test_other_version(self) -> None:
46 text = "t2" + codec.encode(SHARE)[2:]
47 with self.assertRaisesRegex(ShareError, "format 2"):
48 codec.decode(text)
49
50 def test_typo(self) -> None:
51 text = codec.encode(SHARE)
52 typo = text[:10] + ("x" if text[10] != "x" else "y") + text[11:]
53 with self.assertRaisesRegex(ShareError, "checksum"):
54 codec.decode(typo)
55
56 def test_every_single_character_typo_is_caught(self) -> None:
57 text = codec.encode(SHARE)
58 for i, c in enumerate(text):
59 if c == "-":
60 continue
61 for other in codec.ALPHABET:
62 if other == c:
63 continue
64 typo = text[:i] + other + text[i + 1 :]
65 with self.subTest(i=i, other=other), self.assertRaises(ShareError):
66 codec.decode(typo)
67
68 def test_dropped_character_is_caught(self) -> None:
69 text = codec.encode(SHARE)
70 for i, c in enumerate(text):
71 if c != "-":
72 with self.subTest(i=i), self.assertRaises(ShareError):
73 codec.decode(text[:i] + text[i + 1 :])
74
75
76 if __name__ == "__main__":
77 unittest.main()