├── .gitignore ├── .travis.yml ├── AUTHORS ├── LICENSE ├── README.md ├── secretsharing ├── __init__.py ├── polynomials.py ├── primes.py └── sharing.py ├── setup.py └── unit_tests.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # Mr Developer 34 | .mr.developer.cfg 35 | .project 36 | .pydevproject 37 | 38 | ignore -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - "2.7" 5 | 6 | script: python unit_tests.py -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Development Leads 2 | ```````````````` 3 | 4 | - Ryan Shea (https://github.com/rxl) 5 | - Muneeb Ali (https://github.com/muneeb-ali) 6 | 7 | Patches and Suggestions 8 | ```````````````` 9 | 10 | - Michael Flaxman (https://github.com/mflaxman) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Halfmoon Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Secret Sharing 2 | ============= 3 | 4 | [![CircleCI](https://img.shields.io/circleci/project/blockstack/secret-sharing.svg)](https://circleci.com/gh/blockstack/secret-sharing) 5 | [![PyPI](https://img.shields.io/pypi/v/secretsharing.svg)](https://pypi.python.org/pypi/secretsharing/) 6 | [![PyPI](https://img.shields.io/pypi/dm/secretsharing.svg)](https://pypi.python.org/pypi/secretsharing/) 7 | [![PyPI](https://img.shields.io/pypi/l/secretsharing.svg)](https://github.com/onenameio/secret-sharing/blob/master/LICENSE) 8 | [![Slack](http://slack.blockstack.org/badge.svg)](http://slack.blockstack.org/) 9 | 10 | A library for sharding and sharing secrets (like Bitcoin private keys), using shamir's secret sharing scheme. 11 | 12 | ## Installation 13 | 14 | >>> pip install secretsharing 15 | 16 | ## Sample Usage 17 | 18 | ### Hex Secrets 19 | 20 | #### Splitting into shares 21 | 22 | >>> from secretsharing import SecretSharer 23 | >>> shares = SecretSharer.split_secret("c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a", 2, 3) 24 | ['1-58cbd30524507e7a198bdfeb69c8d87fd7d2c10e8d5408851404f7d258cbcea7', '2-ecdbdaea89d75f8e73bde77a46db821cd40f430d39a11c864e5a4868dcb403ed', '3-80ebe2cfef5e40a2cdefef0923ee2bb9d04bc50be5ee308788af98ff609c380a'] 25 | 26 | #### Recovering from shares 27 | 28 | >>> SecretSharer.recover_secret(shares[0:2]) 29 | 'c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a' 30 | 31 | ### Plaintext Secrets 32 | 33 | #### Splitting into shares 34 | 35 | >>> from secretsharing import PlaintextToHexSecretSharer 36 | >>> shares = PlaintextToHexSecretSharer.split_secret("correct horse battery staple", 2, 3) 37 | ['1-7da6b11af146449675780434f6589230a3435d9ab59910354205996f508b8d0d', '2-fb4d6235e28c892cea70367c15ec3cbfed4cf4a417bd01e9812980f3ac97ddc8', '3-78f41350d3d2cdc35f6868c3357fe74f37568bad79e0f39dc04d687808a42d5a'] 38 | 39 | #### Recovering from shares 40 | 41 | >>> PlaintextToHexSecretSharer.recover_secret(shares[0:2]) 42 | 'correct horse battery staple' 43 | 44 | ### Bitcoin Private Keys 45 | 46 | Note: Bitcoin private keys are in [Base58 check](https://en.bitcoin.it/wiki/Base58Check_encoding) format. 47 | 48 | #### Splitting into reliably printable base58 shares 49 | 50 | >>> from secretsharing import BitcoinToB58SecretSharer 51 | >>> shares = BitcoinToB58SecretSharer.split_secret("5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS", 2, 3) 52 | ['2-Bqni1ysZcXhFBhVVJLQgPimDUJrjBrzuvBmc6gPNPh1jyDcvM6uYUuH', '3-9xpMBerBCdHLKzCQ82fjVLfZ3Qt48sqa6nz1E3cc6eu3qUe58vaogU3', '4-85qzMKpnnisRUGuJwivnaxZtcWuP5tgEHQCQMQqqocnMhjfDvkG4t2o'] 53 | 54 | #### Recovering from base58 shares 55 | 56 | >>> BitcoinToB58SecretSharer.recover_secret(shares[0:2]) 57 | '5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS' 58 | 59 | #### Splitting into reliably transcribable [base32](http://en.wikipedia.org/wiki/Base32) shares 60 | 61 | >>> from secretsharing import BitcoinToB32SecretSharer 62 | >>> shares = BitcoinToB32SecretSharer.split_secret("5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS", 2, 3) 63 | ['B-RJ6Y56OSUWDY5VAAGC6XLSTM64CAJ2LPBNB7NKATJCWC7VSHIP5DQIVMR6OGJ4GB', 'C-CT5R24XAR5B732JWYQKSYOYBSF5VHI73HLY24QCFRJR5XUW64C4JWYN6SRGWVCUG', 'D-T54KX27OPEAGZ7TNK5WOFK4WFPZKEXUHNKPWLWDXZQNYPT3WPV3P5IGQTD7HAJDG'] 64 | 65 | #### Recovering from base32 shares 66 | 67 | >>> BitcoinToB32SecretSharer.recover_secret(shares[0:2]) 68 | '5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS' 69 | 70 | #### Splitting into reliably transcribable [zbase32](http://philzimmermann.com/docs/human-oriented-base-32-encoding.txt) shares 71 | 72 | >>> from secretsharing import BitcoinToZB32SecretSharer 73 | >>> shares = BitcoinToZB32SecretSharer.split_secret("5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS", 2, 3) 74 | ['b-aweuzkm9jmfgd7x4k595bzcm3er3epf4dprfwzpprqa3exbuocs9byn4owfuqbo', 'n-btetgqqu8doacarsbyfdzpyycyj6gfdeaaxrpfx33pdjk4ou1d5owjdmdi1iegm9', 'd-njh33f14q7smucmh8iq8uaewc8mzub3mzptrwsegfiz3hc1fozkkjtguc4trh6sq'] 75 | 76 | #### Recovering from zbase32 shares 77 | 78 | >>> BitcoinToZB32SecretSharer.recover_secret(shares[0:2]) 79 | '5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS' 80 | 81 | ### Raw integers 82 | 83 | #### Splitting into shares 84 | 85 | >>> from secretsharing import secret_int_to_points, points_to_secret_int 86 | >>> secret = 88985120633792790105905686761572077713049967498756747774697023364147812997770L 87 | >>> shares = secret_int_to_points(secret, 2, 3) 88 | [(1, 108834987130598118322155382953070549297972563210322923466700361825476188819879L), (2, 12892764390087251114834094135881113029625174256248535119246116278891435001755L), (3, 32742630886892579331083790327379584614547769967814710811249454740219810823864L)] 89 | 90 | #### Recovering from shares 91 | 92 | >>> points_to_secret_int(shares[0:2]) 93 | 88985120633792790105905686761572077713049967498756747774697023364147812997770L 94 | 95 | ### Custom formats 96 | 97 | #### Splitting into shares 98 | 99 | >>> from secretsharing import SecretSharer, base64_chars 100 | >>> sharer_class = SecretSharer 101 | >>> sharer_class.share_charset = base64_chars 102 | >>> shares = sharer_class.split_secret("c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a", 2, 3) 103 | ['B-JpxCTUQ9D+q93JglQM9yRinI2Cyxe92FTBSYa93ppfY', 'C-HAmR0pjHuHwL4rozXnFY05ysIJVqtf3pob1HCMaaZUm', 'D-EXbhV+1SYQ1Z6NxBfBM/YQ+PaP4j8B5N92X1pa9LJJ0'] 104 | 105 | #### Recovering from shares 106 | 107 | >>> sharer_class.recover_secret(shares[0:2]) 108 | 'c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a' 109 | -------------------------------------------------------------------------------- /secretsharing/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Secret Sharing 4 | ~~~~~ 5 | 6 | :copyright: (c) 2014 by Halfmoon Labs 7 | :license: MIT, see LICENSE for more details. 8 | """ 9 | 10 | __version__ = '0.2.7' 11 | 12 | from .sharing import secret_int_to_points, points_to_secret_int, \ 13 | point_to_share_string, share_string_to_point, SecretSharer, \ 14 | HexToHexSecretSharer, PlaintextToHexSecretSharer, \ 15 | BitcoinToB58SecretSharer, BitcoinToB32SecretSharer, \ 16 | BitcoinToZB32SecretSharer 17 | -------------------------------------------------------------------------------- /secretsharing/polynomials.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Secret Sharing 4 | ~~~~~ 5 | 6 | :copyright: (c) 2014 by Halfmoon Labs 7 | :license: MIT, see LICENSE for more details. 8 | """ 9 | 10 | from utilitybelt import secure_randint as randint 11 | 12 | 13 | def egcd(a, b): 14 | if a == 0: 15 | return (b, 0, 1) 16 | else: 17 | g, y, x = egcd(b % a, a) 18 | return (g, x - (b // a) * y, y) 19 | 20 | 21 | def mod_inverse(k, prime): 22 | k = k % prime 23 | if k < 0: 24 | r = egcd(prime, -k)[2] 25 | else: 26 | r = egcd(prime, k)[2] 27 | return (prime + r) % prime 28 | 29 | 30 | def random_polynomial(degree, intercept, upper_bound): 31 | """ Generates a random polynomial with positive coefficients. 32 | """ 33 | if degree < 0: 34 | raise ValueError('Degree must be a non-negative number.') 35 | coefficients = [intercept] 36 | for i in range(degree): 37 | random_coeff = randint(0, upper_bound-1) 38 | coefficients.append(random_coeff) 39 | return coefficients 40 | 41 | 42 | def get_polynomial_points(coefficients, num_points, prime): 43 | """ Calculates the first n polynomial points. 44 | [ (1, f(1)), (2, f(2)), ... (n, f(n)) ] 45 | """ 46 | points = [] 47 | for x in range(1, num_points+1): 48 | # start with x=1 and calculate the value of y 49 | y = coefficients[0] 50 | # calculate each term and add it to y, using modular math 51 | for i in range(1, len(coefficients)): 52 | exponentiation = (x**i) % prime 53 | term = (coefficients[i] * exponentiation) % prime 54 | y = (y + term) % prime 55 | # add the point to the list of points 56 | points.append((x, y)) 57 | return points 58 | 59 | 60 | def modular_lagrange_interpolation(x, points, prime): 61 | # break the points up into lists of x and y values 62 | x_values, y_values = zip(*points) 63 | # initialize f(x) and begin the calculation: f(x) = SUM( y_i * l_i(x) ) 64 | f_x = 0 65 | for i in range(len(points)): 66 | # evaluate the lagrange basis polynomial l_i(x) 67 | numerator, denominator = 1, 1 68 | for j in range(len(points)): 69 | # don't compute a polynomial fraction if i equals j 70 | if i == j: 71 | continue 72 | # compute a fraction & update the existing numerator + denominator 73 | numerator = (numerator * (x - x_values[j])) % prime 74 | denominator = (denominator * (x_values[i] - x_values[j])) % prime 75 | # get the polynomial from the numerator + denominator mod inverse 76 | lagrange_polynomial = numerator * mod_inverse(denominator, prime) 77 | # multiply the current y & the evaluated polynomial & add it to f(x) 78 | f_x = (prime + f_x + (y_values[i] * lagrange_polynomial)) % prime 79 | return f_x 80 | -------------------------------------------------------------------------------- /secretsharing/primes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Secret Sharing 4 | ~~~~~ 5 | 6 | :copyright: (c) 2014 by Halfmoon Labs 7 | :license: MIT, see LICENSE for more details. 8 | """ 9 | 10 | 11 | def calculate_mersenne_primes(): 12 | """ Returns all the mersenne primes with less than 500 digits. 13 | All primes: 14 | 3, 7, 31, 127, 8191, 131071, 524287, 2147483647L, 2305843009213693951L, 15 | 618970019642690137449562111L, 162259276829213363391578010288127L, 16 | 170141183460469231731687303715884105727L, 17 | 68647976601306097149...12574028291115057151L, (157 digits) 18 | 53113799281676709868...70835393219031728127L, (183 digits) 19 | 10407932194664399081...20710555703168729087L, (386 digits) 20 | """ 21 | mersenne_prime_exponents = [ 22 | 2, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127, 521, 607, 1279 23 | ] 24 | primes = [] 25 | for exp in mersenne_prime_exponents: 26 | prime = 1 27 | for i in range(exp): 28 | prime *= 2 29 | prime -= 1 30 | primes.append(prime) 31 | return primes 32 | 33 | SMALLEST_257BIT_PRIME = (2**256 + 297) 34 | SMALLEST_321BIT_PRIME = (2**320 + 27) 35 | SMALLEST_385BIT_PRIME = (2**384 + 231) 36 | STANDARD_PRIMES = calculate_mersenne_primes() + [ 37 | SMALLEST_257BIT_PRIME, SMALLEST_321BIT_PRIME, SMALLEST_385BIT_PRIME 38 | ] 39 | STANDARD_PRIMES.sort() 40 | 41 | 42 | def get_large_enough_prime(batch): 43 | """ Returns a prime number that is greater all the numbers in the batch. 44 | """ 45 | # build a list of primes 46 | primes = STANDARD_PRIMES 47 | # find a prime that is greater than all the numbers in the batch 48 | for prime in primes: 49 | numbers_greater_than_prime = [i for i in batch if i > prime] 50 | if len(numbers_greater_than_prime) == 0: 51 | return prime 52 | return None 53 | -------------------------------------------------------------------------------- /secretsharing/sharing.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Secret Sharing 4 | ~~~~~ 5 | 6 | :copyright: (c) 2014 by Halfmoon Labs 7 | :license: MIT, see LICENSE for more details. 8 | """ 9 | 10 | import string 11 | 12 | from six import integer_types 13 | from utilitybelt import int_to_charset, charset_to_int, base58_chars, \ 14 | base32_chars, zbase32_chars 15 | from .primes import get_large_enough_prime 16 | from .polynomials import random_polynomial, \ 17 | get_polynomial_points, modular_lagrange_interpolation 18 | 19 | 20 | def secret_int_to_points(secret_int, point_threshold, num_points, prime=None): 21 | """ Split a secret (integer) into shares (pair of integers / x,y coords). 22 | 23 | Sample the points of a random polynomial with the y intercept equal to 24 | the secret int. 25 | """ 26 | if point_threshold < 2: 27 | raise ValueError("Threshold must be >= 2.") 28 | if point_threshold > num_points: 29 | raise ValueError("Threshold must be < the total number of points.") 30 | if not prime: 31 | prime = get_large_enough_prime([secret_int, num_points]) 32 | if not prime: 33 | raise ValueError("Error! Secret is too long for share calculation!") 34 | coefficients = random_polynomial(point_threshold-1, secret_int, prime) 35 | points = get_polynomial_points(coefficients, num_points, prime) 36 | return points 37 | 38 | 39 | def points_to_secret_int(points, prime=None): 40 | """ Join int points into a secret int. 41 | 42 | Get the intercept of a random polynomial defined by the given points. 43 | """ 44 | if not isinstance(points, list): 45 | raise ValueError("Points must be in list form.") 46 | for point in points: 47 | if not isinstance(point, tuple) and len(point) == 2: 48 | raise ValueError("Each point must be a tuple of two values.") 49 | if not (isinstance(point[0], integer_types) and 50 | isinstance(point[1], integer_types)): 51 | raise ValueError("Each value in the point must be an int.") 52 | x_values, y_values = zip(*points) 53 | if not prime: 54 | prime = get_large_enough_prime(y_values) 55 | free_coefficient = modular_lagrange_interpolation(0, points, prime) 56 | secret_int = free_coefficient # the secret int is the free coefficient 57 | return secret_int 58 | 59 | 60 | def point_to_share_string(point, charset): 61 | """ Convert a point (a tuple of two integers) into a share string - that is, 62 | a representation of the point that uses the charset provided. 63 | """ 64 | # point should be in the format (1, 4938573982723...) 65 | if '-' in charset: 66 | raise ValueError( 67 | 'The character "-" cannot be in the supplied charset.') 68 | if not (isinstance(point, tuple) and len(point) == 2 and 69 | isinstance(point[0], integer_types) and 70 | isinstance(point[1], integer_types)): 71 | raise ValueError( 72 | 'Point format is invalid. Must be a pair of integers.') 73 | x, y = point 74 | x_string = int_to_charset(x, charset) 75 | y_string = int_to_charset(y, charset) 76 | share_string = x_string + '-' + y_string 77 | return share_string 78 | 79 | 80 | def share_string_to_point(share_string, charset): 81 | """ Convert a share string to a point (a tuple of integers). 82 | """ 83 | # share should be in the format "01-d051080de7..." 84 | if '-' in charset: 85 | raise ValueError( 86 | 'The character "-" cannot be in the supplied charset.') 87 | if not isinstance(share_string, str) and share_string.count('-') == 1: 88 | raise ValueError('Share format is invalid.') 89 | x_string, y_string = share_string.split('-') 90 | if (set(x_string) - set(charset)) or (set(y_string) - set(charset)): 91 | raise ValueError("Share has characters that aren't in the charset.") 92 | x = charset_to_int(x_string, charset) 93 | y = charset_to_int(y_string, charset) 94 | return (x, y) 95 | 96 | 97 | class SecretSharer(): 98 | """ Creates a secret sharer, which can convert from a secret string to a 99 | list of shares and vice versa. The splitter is initialized with the 100 | character set of the secrets and the character set of the shares that 101 | it expects to be dealing with. 102 | """ 103 | secret_charset = string.hexdigits[0:16] 104 | share_charset = string.hexdigits[0:16] 105 | 106 | def __init__(self): 107 | pass 108 | 109 | @classmethod 110 | def split_secret(cls, secret_string, share_threshold, num_shares): 111 | secret_int = charset_to_int(secret_string, cls.secret_charset) 112 | points = secret_int_to_points(secret_int, share_threshold, num_shares) 113 | shares = [] 114 | for point in points: 115 | shares.append(point_to_share_string(point, cls.share_charset)) 116 | return shares 117 | 118 | @classmethod 119 | def recover_secret(cls, shares): 120 | points = [] 121 | for share in shares: 122 | points.append(share_string_to_point(share, cls.share_charset)) 123 | secret_int = points_to_secret_int(points) 124 | secret_string = int_to_charset(secret_int, cls.secret_charset) 125 | return secret_string 126 | 127 | 128 | class HexToHexSecretSharer(SecretSharer): 129 | """ Standard sharer for converting hex secrets to hex shares. 130 | """ 131 | secret_charset = string.hexdigits[0:16] 132 | share_charset = string.hexdigits[0:16] 133 | 134 | 135 | class PlaintextToHexSecretSharer(SecretSharer): 136 | """ Good for converting secret messages into standard hex shares. 137 | """ 138 | secret_charset = string.printable 139 | share_charset = string.hexdigits[0:16] 140 | 141 | 142 | class BitcoinToB58SecretSharer(SecretSharer): 143 | """ Good for converting Bitcoin secret keys into shares that can be 144 | reliably printed out in any font. 145 | """ 146 | secret_charset = base58_chars 147 | share_charset = base58_chars 148 | 149 | 150 | class BitcoinToB32SecretSharer(SecretSharer): 151 | """ Good for converting Bitcoin secret keys into shares that can be 152 | reliably and conveniently transcribed. 153 | """ 154 | secret_charset = base58_chars 155 | share_charset = base32_chars 156 | 157 | 158 | class BitcoinToZB32SecretSharer(SecretSharer): 159 | """ Good for converting Bitcoin secret keys into shares that can be 160 | reliably and conveniently transcribed. 161 | """ 162 | secret_charset = base58_chars 163 | share_charset = zbase32_chars 164 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | Secret Sharing 3 | ============== 4 | 5 | """ 6 | 7 | from setuptools import setup 8 | 9 | setup( 10 | name='secretsharing', 11 | version='0.2.6', 12 | url='https://github.com/onenameio/secret-sharing', 13 | license='MIT', 14 | author='Halfmoon Labs', 15 | author_email='hello@halfmoon.io', 16 | description=("Tools for sharing secrets (like Bitcoin private keys), " 17 | "using shamir's secret sharing scheme."), 18 | packages=[ 19 | 'secretsharing', 20 | ], 21 | zip_safe=False, 22 | install_requires=[ 23 | 'six', 24 | 'utilitybelt', 25 | ], 26 | classifiers=[ 27 | 'Intended Audience :: Developers', 28 | 'License :: OSI Approved :: MIT License', 29 | 'Operating System :: OS Independent', 30 | 'Programming Language :: Python :: 2.7', 31 | 'Programming Language :: Python :: 3.4', 32 | ], 33 | ) 34 | -------------------------------------------------------------------------------- /unit_tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Secret Sharing 4 | ~~~~~ 5 | 6 | :copyright: (c) 2014 by Halfmoon Labs 7 | :license: MIT, see LICENSE for more details. 8 | """ 9 | 10 | import random 11 | import unittest 12 | from test import test_support 13 | from utilitybelt import base64_chars 14 | from secretsharing import secret_int_to_points, points_to_secret_int, \ 15 | point_to_share_string, share_string_to_point, SecretSharer, \ 16 | HexToHexSecretSharer, PlaintextToHexSecretSharer, \ 17 | BitcoinToB58SecretSharer, BitcoinToB32SecretSharer, \ 18 | BitcoinToZB32SecretSharer 19 | 20 | 21 | class ShamirSharingTest(unittest.TestCase): 22 | def setUp(self): 23 | pass 24 | 25 | def tearDown(self): 26 | pass 27 | 28 | def split_and_recover_secret(self, sharer_class, m, n, secret): 29 | shares = sharer_class.split_secret(secret, m, n) 30 | random.shuffle(shares) 31 | recovered_secret = sharer_class.recover_secret(shares[0:m]) 32 | assert(recovered_secret == secret) 33 | 34 | def test_hex_to_hex_sharing(self): 35 | recovered_secret = self.split_and_recover_secret( 36 | SecretSharer, 3, 5, 37 | "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a") 38 | 39 | def test_printable_ascii_to_hex_sharing(self): 40 | recovered_secret = self.split_and_recover_secret( 41 | PlaintextToHexSecretSharer, 3, 5, 42 | "correct horse battery staple") 43 | 44 | def test_b58_to_b32_sharing(self): 45 | recovered_secret = self.split_and_recover_secret( 46 | BitcoinToB32SecretSharer, 3, 5, 47 | "5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS") 48 | 49 | def test_b58_to_zb32_sharing(self): 50 | recovered_secret = self.split_and_recover_secret( 51 | BitcoinToZB32SecretSharer, 3, 5, 52 | "5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS") 53 | 54 | def test_b58_to_b58_sharing(self): 55 | recovered_secret = self.split_and_recover_secret( 56 | BitcoinToB58SecretSharer, 3, 5, 57 | "5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS") 58 | 59 | def test_hex_to_base64_sharing(self): 60 | sharer_class = SecretSharer 61 | sharer_class.share_charset = base64_chars 62 | recovered_secret = self.split_and_recover_secret( 63 | sharer_class, 3, 5, 64 | "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a") 65 | 66 | def test_2_of_3_sharing(self): 67 | recovered_secret = self.split_and_recover_secret( 68 | SecretSharer, 2, 3, 69 | "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a") 70 | 71 | def test_4_of_7_sharing(self): 72 | recovered_secret = self.split_and_recover_secret( 73 | SecretSharer, 4, 7, 74 | "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a") 75 | 76 | def test_5_of_9_sharing(self): 77 | recovered_secret = self.split_and_recover_secret( 78 | SecretSharer, 5, 9, 79 | "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a") 80 | 81 | def test_2_of_2_sharing(self): 82 | recovered_secret = self.split_and_recover_secret( 83 | SecretSharer, 2, 2, 84 | "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a") 85 | 86 | 87 | def test_main(): 88 | test_support.run_unittest( 89 | ShamirSharingTest 90 | ) 91 | 92 | 93 | if __name__ == '__main__': 94 | test_main() --------------------------------------------------------------------------------