shea256 / secret-sharing

A system for securely splitting secrets with Shamir's Secret Sharing Scheme
MIT License
483 stars 143 forks source link

additive homomorphism in raw integer mode reconstructs irreliably #31

Closed degregat closed 5 years ago

degregat commented 5 years ago

I wanted to use the additively homomorphic property of the secret sharing, so I wrote a little toy program to generate two sets of shares from two secrets, add them and reconstruct the sum from the new summed shares. The reconstruction works correctly only some of the time though. There also seems to be some pattern to the incorrect reconstruction.

This is the program:

from secretsharing import secret_int_to_points, points_to_secret_int

secret1 = 5
shares1 = secret_int_to_points(secret1, 2, 3)
print(secret1)
print(shares1)

secret2 = 7
shares2 = secret_int_to_points(secret2, 2, 3)
print(secret2)
print(shares2)

secretsum = [ (x1, (y1+y2)) for ((x1,y1),(x2,y2)) in zip(shares1, shares2) ]
print(secretsum)

plainsum = points_to_secret_int(secretsum[0:2])
print(plainsum)

I appended some of the outputs, with rough estimates of how often they occur:

With the two secrets being 5 and 7 about ~20% of the time I get a correct result.

5
[(1, 4L), (2, 3L), (3, 2L)]
7
[(1, 6L), (2, 5L), (3, 4L)]
[(1, 10L), (2, 8L), (3, 6L)]
12

Maybe ~60% of the time I get output similar to this:

5
[(1, 1L), (2, 4L), (3, 0L)]
7
[(1, 0L), (2, 0L), (3, 0L)]
[(1, 1L), (2, 4L), (3, 0L)]
5

And the other ~20% these two have popped up so far.

5
[(1, 6L), (2, 0L), (3, 1L)]
7
[(1, 4L), (2, 1L), (3, 5L)]
[(1, 10L), (2, 1L), (3, 6L)]
19
5
[(1, 2L), (2, 6L), (3, 3L)]
7
[(1, 2L), (2, 4L), (3, 6L)]
[(1, 4L), (2, 10L), (3, 9L)]
29

With secrets 4 and 8, ~80% of the time I get a correct result.

4
[(1, 5L), (2, 6L), (3, 0L)]
8
[(1, 14L), (2, 20L), (3, 26L)]
[(1, 19L), (2, 26L), (3, 26L)]
12

And ~20% of the time I get this.

4
[(1, 0L), (2, 3L), (3, 6L)]
8
[(1, 22L), (2, 5L), (3, 19L)]
[(1, 22L), (2, 8L), (3, 25L)]
5
degregat commented 5 years ago

I found the source: If no prime is explicitly given, primes.get_large_enough_prime selects a prime big enough for each individual share generation, so added shares can be greater than this prime.

The fix is to pass a prime bigger than any possible sum as an additional argument. This seems to be doable in recent versions, but not yet in 0.2.6 which I was using.