From 9391c8ce4110096278690953e860e66c06184485 Mon Sep 17 00:00:00 2001 From: John-Mark Gurney Date: Wed, 31 May 2023 23:28:33 -0700 Subject: [PATCH] improve docs, handle some edge cases, use generator instead of list also hard code the reduction table, but check it in the tests... --- shamirss.py | 45 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/shamirss.py b/shamirss.py index 076b681..72ed64e 100644 --- a/shamirss.py +++ b/shamirss.py @@ -31,7 +31,7 @@ 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 +the same length as the secret. This limits the number of shares to 255. Sample usage: @@ -115,12 +115,17 @@ def recover_data(shares, k): if len(shares) < k: raise ValueError('not enough shares to recover') - return bytes([ int(sum([ GF2p8(y[idx]) * - functools.reduce(operator.mul, [ pix * ((GF2p8(pix) - x) ** -1) for - pix, piy in shares[:k] if pix != x ], 1) for x, y in shares[:k] ], + return bytes([ int(sum(( GF2p8(y[idx]) * + functools.reduce(operator.mul, ( pix * ((GF2p8(pix) - x) ** -1) for + pix, piy in shares[:k] if pix != x ), 1) for x, y in shares[:k] ), 0)) for idx in range(len(shares[0][1]))]) class GF2p8: + # polynomial 0x187 + '''An implementation of GF(2^8). It uses the polynomial 0x187 + or x^8 + x^7 + x^2 + x + 1. + ''' + _invcache = (None, 1, 195, 130, 162, 126, 65, 90, 81, 54, 63, 172, 227, 104, 45, 42, 235, 155, 27, 53, 220, 30, 86, 165, 178, 116, 52, 18, 213, 100, 21, 221, 182, 75, 142, 251, 206, 233, 217, 161, 110, 219, 15, 44, 43, 14, 145, 241, 89, 215, 58, 244, 26, 19, 9, 80, 169, 99, 50, 245, 201, 204, 173, 10, 91, 6, 230, 247, 71, 191, 190, 68, 103, 123, 183, 33, 175, 83, 147, 255, 55, 8, 174, 77, 196, 209, 22, 164, 214, 48, 7, 64, 139, 157, 187, 140, 239, 129, 168, 57, 29, 212, 122, 72, 13, 226, 202, 176, 199, 222, 40, 218, 151, 210, 242, 132, 25, 179, 185, 135, 167, 228, 102, 73, 149, 153, 5, 163, 238, 97, 3, 194, 115, 243, 184, 119, 224, 248, 156, 92, 95, 186, 34, 250, 240, 46, 254, 78, 152, 124, 211, 112, 148, 125, 234, 17, 138, 93, 188, 236, 216, 39, 4, 127, 87, 23, 229, 120, 98, 56, 171, 170, 11, 62, 82, 76, 107, 203, 24, 117, 192, 253, 32, 74, 134, 118, 141, 94, 158, 237, 70, 69, 180, 252, 131, 2, 84, 208, 223, 108, 205, 60, 106, 177, 61, 200, 36, 232, 197, 85, 113, 150, 101, 28, 88, 49, 160, 38, 111, 41, 20, 31, 109, 198, 136, 249, 105, 12, 121, 166, 66, 246, 207, 37, 154, 16, 159, 189, 128, 96, 144, 47, 114, 133, 51, 59, 231, 67, 137, 225, 143, 35, 193, 181, 146, 79) @staticmethod @@ -136,14 +141,26 @@ class GF2p8: return r - # polynomial 0x187 - _reduce = tuple(_makered(x, 0x87) for x in range(0, 16)) + _reduce = (0, 135, 137, 14, 149, 18, 28, 155, 173, 42, 36, 163, 56, 191, 177, 54) def __init__(self, v): - if v >= 256: + '''v must be in the range [ 0, 255 ]. + + Create an element of GF(2^8). + + The operators have been overloaded, so most normal math works. + + It will also automatically promote non-GF2p8 numbers if + possible, e.g. GF2p8(5) + 10 works. + ''' + + if v >= 256 or v < 0: raise ValueError('%d is not a member of GF(2^8)' % v) - self._v = v + self._v = int(v) + + if self._v != v: + raise ValueError('%d is not a member of GF(2^8)' % v) # basic operations def __add__(self, o): @@ -268,11 +285,19 @@ class TestShamirSS(unittest.TestCase): for i in range(5): self.assertEqual(val, recover_data([ a[j] for j in random.sample(range(30), 15) ], 15)) + def test_gf2p8_reduce(self): + reduce = tuple(_makered(x, 0x87) for x in range(0, 16)) + + if GF2p8._reduce != reduce: # pragma: no cover + print('reduce:', repr(reduce)) + self.assertEqual(GF2p8._reduce, reduce) + + def test_gf2p8_inv(self): a = GF2p8(random.randint(0, 255)) - with unittest.mock.patch.object(GF2p8, '_invcache', []) as pinvc: + with unittest.mock.patch.object(GF2p8, '_invcache', ()) as pinvc: ainv = a ** -1 self.assertEqual(a * ainv, 1) @@ -302,6 +327,8 @@ class TestShamirSS(unittest.TestCase): def test_gf2p8_errors(self): self.assertRaises(ValueError, GF2p8, 1000) + self.assertRaises(ValueError, GF2p8, 40.5) + self.assertRaises(ValueError, GF2p8, -1) def test_gf2p8(self): self.assertEqual(int(GF2p8(5)), 5)