Skip to content

Commit f987712

Browse files
committed
function to add new shares to a set of existing ones
1 parent 7828798 commit f987712

File tree

3 files changed

+108
-0
lines changed

3 files changed

+108
-0
lines changed

SSSA/sssa.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,30 @@ def combine(self, shares):
6969
secret[part_index] = (secret[part_index] + working) % self.util.prime
7070

7171
return self.util.merge_ints(secret)
72+
73+
def add_share(self, shares):
74+
# The "raw" secret is padded with \x00 and split into 32 char (64 hex) chunks: for each part there will be a polynomial.
75+
# The result of 'create' still has "shares" entries, but each is a \sum_i^parts x_i+y_i.
76+
# Reconstruct points on the polynomial(s) from the shares
77+
x,y=self.util.shares_to_x_y(shares)
78+
79+
# Initialize known numbers
80+
numbers = []
81+
for parts in x:
82+
numbers.extend(parts)
83+
84+
# For each polynomial, interpolate another point
85+
new_share = ""
86+
for pol_nr in range(len(x)):
87+
value = self.util.random()
88+
while value in numbers:
89+
value = self.util.random()
90+
numbers.append(value)
91+
92+
y_interpolated = self.util.lagrange_interpolate(value,x[pol_nr],y[pol_nr],self.util.prime)
93+
94+
new_share += self.util.to_base64(value)
95+
new_share += self.util.to_base64(y_interpolated)
96+
97+
shares.append(new_share)
98+
return shares

SSSA/utils.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,72 @@ def mod_inverse(self, number):
107107
if number < 0:
108108
remainder *= -1
109109
return (self.prime + remainder) % self.prime
110+
111+
112+
def shares_to_x_y(self,shares):
113+
x = []
114+
y = []
115+
# must be: len(shares[0])//88 == len(shares[0])/88
116+
for i in range(len(shares[0])//88):
117+
part_x = []
118+
part_y = []
119+
for share in shares:
120+
part_x.append(self.from_base64(share[88*i+0:88*i+44]))
121+
part_y.append(self.from_base64(share[88*i+44:88*i+88]))
122+
x.append(tuple(part_x))
123+
y.append(tuple(part_y))
124+
return x,y
125+
126+
# extended_gcd, divmod, lagrange_interpolate are copied from https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing
127+
128+
def extended_gcd(self, a, b):
129+
"""
130+
Division in integers modulus p means finding the inverse of the
131+
denominator modulo p and then multiplying the numerator by this
132+
inverse (Note: inverse of A is B such that A*B % p == 1) this can
133+
be computed via extended Euclidean algorithm
134+
http://en.wikipedia.org/wiki/Modular_multiplicative_inverse#Computation
135+
"""
136+
x = 0
137+
last_x = 1
138+
y = 1
139+
last_y = 0
140+
while b != 0:
141+
quot = a // b
142+
a, b = b, a % b
143+
x, last_x = last_x - quot * x, x
144+
y, last_y = last_y - quot * y, y
145+
return last_x, last_y
146+
147+
def divmod(self, num, den, p):
148+
"""Compute num / den modulo prime p
149+
150+
To explain what this means, the return value will be such that
151+
the following is true: den * divmod(num, den, p) % p == num
152+
"""
153+
inv, _ = self.extended_gcd(den, p)
154+
return num * inv
155+
156+
def lagrange_interpolate(self, x, x_s, y_s, p):
157+
"""
158+
Find the y-value for the given x, given n (x, y) points;
159+
k points will define a polynomial of up to kth order.
160+
"""
161+
k = len(x_s)
162+
assert k == len(set(x_s)), "points must be distinct"
163+
def PI(vals): # upper-case PI -- product of inputs
164+
accum = 1
165+
for v in vals:
166+
accum *= v
167+
return accum
168+
nums = [] # avoid inexact division
169+
dens = []
170+
for i in range(k):
171+
others = list(x_s)
172+
cur = others.pop(i)
173+
nums.append(PI(x - o for o in others))
174+
dens.append(PI(cur - o for o in others))
175+
den = PI(dens)
176+
num = sum([self.divmod(nums[i] * den * y_s[i] % p, dens[i], p)
177+
for i in range(k)])
178+
return (self.divmod(num, den, p) + p) % p

sssa_tests.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,17 @@ def test_library_combine(self):
1818

1919
self.assertEqual(sss.combine(shares), "test-pass")
2020

21+
def test_add_share(self):
22+
secret='the cake is a lie'
23+
sss = sssa()
24+
shares = sss.create(3,5,secret)
25+
self.assertEqual(len(shares),5)
26+
# restore secret using original shares
27+
self.assertEqual(sss.combine(shares[:3]),secret)
28+
# add new share and restore secret with a mix of original and new share
29+
new_shares = sss.add_share(shares)
30+
self.assertEqual(len(new_shares),6)
31+
self.assertEqual(sss.combine(new_shares[-3:]),secret)
32+
2133
if __name__ == '__main__':
2234
unittest.main()

0 commit comments

Comments
 (0)