Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support generating new shares for the secret #25

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions secretsharing/primes.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def calculate_mersenne_primes():
primes.append(prime)
return primes


SMALLEST_257BIT_PRIME = (2**256 + 297)
SMALLEST_321BIT_PRIME = (2**320 + 27)
SMALLEST_385BIT_PRIME = (2**384 + 231)
Expand Down
61 changes: 56 additions & 5 deletions secretsharing/sharing.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def share_string_to_point(share_string, charset):
raise ValueError("Share has characters that aren't in the charset.")
x = charset_to_int(x_string, charset)
y = charset_to_int(y_string, charset)
return (x, y)
return x, y


class SecretSharer():
Expand All @@ -110,10 +110,7 @@ def __init__(self):
def split_secret(cls, secret_string, share_threshold, num_shares):
secret_int = charset_to_int(secret_string, cls.secret_charset)
points = secret_int_to_points(secret_int, share_threshold, num_shares)
shares = []
for point in points:
shares.append(point_to_share_string(point, cls.share_charset))
return shares
return cls.points_to_shares(points)

@classmethod
def recover_secret(cls, shares):
Expand All @@ -124,6 +121,13 @@ def recover_secret(cls, shares):
secret_string = int_to_charset(secret_int, cls.secret_charset)
return secret_string

@classmethod
def points_to_shares(cls, points):
shares = []
for point in points:
shares.append(point_to_share_string(point, cls.share_charset))
return shares


class HexToHexSecretSharer(SecretSharer):
""" Standard sharer for converting hex secrets to hex shares.
Expand Down Expand Up @@ -161,3 +165,50 @@ class BitcoinToZB32SecretSharer(SecretSharer):
"""
secret_charset = base58_chars
share_charset = zbase32_chars


class SecretSharerNew(SecretSharer):
"""
This class can be used to generate new shares for a secret once some
shares have already been generated. eg. Alice decides to shard her secret
in 5-of-10, later she decides that 10 more shares should be generated for
the same secret making it 5-of-15. Any of the old shares and the newly
generated shares can be used together
"""
def __init__(self):
# Holds information for each secret
self.secrets = {}

def generate_shares(self, secret_string, share_threshold, num_shares,
max_shares=1000):
if max_shares > 1000:
raise ValueError('Why do you want greater than 1000 shares')
if secret_string not in self.secrets:
self._generate_and_record_params_for_secret(secret_string,
share_threshold,
max_shares)
secret_int, prime, coefficients = self._get_params_for_secret(
secret_string)
points = get_polynomial_points(coefficients, num_shares, prime)
return self.points_to_shares(points)

def _generate_and_record_params_for_secret(self, secret_string,
share_threshold, max_shares):
secret_int = charset_to_int(secret_string, self.secret_charset)
prime = get_large_enough_prime([secret_int, max_shares])
coefficients = random_polynomial(share_threshold - 1, secret_int, prime)
self._record_params_for_secret(secret_string, secret_int, prime,
coefficients)

def _record_params_for_secret(self, secret_string, secret_int, prime,
coefficients):
self.secrets[secret_string] = {
'int': secret_int,
'prime': prime,
'coefficients': coefficients
}

def _get_params_for_secret(self, secret_string):
return self.secrets[secret_string]['int'], \
self.secrets[secret_string]['prime'], \
self.secrets[secret_string]['coefficients']
54 changes: 49 additions & 5 deletions unit_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@

import random
import unittest
from test import test_support
from test import support
from utilitybelt import base64_chars
from secretsharing import secret_int_to_points, points_to_secret_int, \
point_to_share_string, share_string_to_point, SecretSharer, \
HexToHexSecretSharer, PlaintextToHexSecretSharer, \
BitcoinToB58SecretSharer, BitcoinToB32SecretSharer, \
BitcoinToZB32SecretSharer
from secretsharing.sharing import SecretSharerNew


class ShamirSharingTest(unittest.TestCase):
Expand All @@ -25,11 +26,12 @@ def setUp(self):
def tearDown(self):
pass

def split_and_recover_secret(self, sharer_class, m, n, secret):
@staticmethod
def split_and_recover_secret(sharer_class, m, n, secret):
shares = sharer_class.split_secret(secret, m, n)
random.shuffle(shares)
recovered_secret = sharer_class.recover_secret(shares[0:m])
assert(recovered_secret == secret)
assert (recovered_secret == secret)

def test_hex_to_hex_sharing(self):
recovered_secret = self.split_and_recover_secret(
Expand Down Expand Up @@ -84,9 +86,51 @@ def test_2_of_2_sharing(self):
"c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a")


class ShamirSharingNewTest(unittest.TestCase):
def test_4_of_7_sharing(self):
# A simple test works
ShamirSharingTest.split_and_recover_secret(
SecretSharerNew, 4, 7,
"c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a")

def test_5_of_30_sharing(self):
# Generate new shares for the same secret
secret = "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a"
sharer = SecretSharerNew()

# Shares can recover secret
shares_10 = sharer.generate_shares(secret, 5, 10)
assert SecretSharerNew.recover_secret(shares_10) == secret
shares_15 = sharer.generate_shares(secret, 5, 15)
assert SecretSharerNew.recover_secret(shares_15) == secret
shares_25 = sharer.generate_shares(secret, 5, 25)
assert SecretSharerNew.recover_secret(shares_25) == secret
shares_30 = sharer.generate_shares(secret, 5, 30)
assert SecretSharerNew.recover_secret(shares_30) == secret
shares_50 = sharer.generate_shares(secret, 5, 50)
assert SecretSharerNew.recover_secret(shares_50) == secret

# Mix shares from different `generate_shares` calls
# Ranges for all except `shares_10` are used so that shares don't repeat
assert SecretSharerNew.recover_secret(random.sample(shares_10, 2) +
random.sample(shares_15[10:15], 3)) == secret
assert SecretSharerNew.recover_secret(random.sample(shares_15, 1) +
random.sample(shares_25[15:25],
4)) == secret

assert SecretSharerNew.recover_secret([random.sample(i, 1)[0]
for i in [shares_10,
shares_15[10:15],
shares_25[15:25],
shares_30[25:30],
shares_50[30:50]
]]) == secret


def test_main():
test_support.run_unittest(
ShamirSharingTest
support.run_unittest(
ShamirSharingTest,
ShamirSharingNewTest
)


Expand Down