also hard code the reduction table, but check it in the tests...main
@@ -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) | |||