-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
nginsburg
committed
Sep 24, 2019
1 parent
d6d28f1
commit 2ac41fa
Showing
9 changed files
with
577 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
Development Lead | ||
```````````````` | ||
|
||
- Noah Ginsburg (https://github.com/ginsburgnm) <[email protected]> | ||
|
||
Initially Dev Work | ||
`````````````````` | ||
|
||
- Ryan Shea (https://github.com/rxl) <[email protected]> | ||
- Muneeb Ali (https://github.com/muneeb-ali) <[email protected]> | ||
|
||
Patches and Suggestions | ||
```````````````` | ||
|
||
- Michael Flaxman (https://github.com/mflaxman) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
Secret Sharing | ||
============= | ||
|
||
[![PyPI](https://img.shields.io/pypi/v/pyseltongue.svg)](https://pypi.python.org/pypi/pyseltongue/) | ||
[![PyPI](https://img.shields.io/pypi/dm/pyseltongue.svg)](https://pypi.python.org/pypi/pyseltongue/) | ||
[![PyPI](https://img.shields.io/pypi/l/pyseltongue.svg)](https://github.com/ginsburgnm/pyseltongue/blob/master/LICENSE) | ||
|
||
A library for sharding and sharing secrets (like Bitcoin private keys), using shamir's secret sharing scheme. | ||
|
||
## Installation | ||
|
||
>>> pip install pyseltongue | ||
|
||
## Sample Usage | ||
|
||
### Hex Secrets | ||
|
||
#### Splitting into shares | ||
|
||
>>> from pyseltongue import SecretSharer | ||
>>> shares = SecretSharer.split_secret("c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a", 2, 3) | ||
['1-58cbd30524507e7a198bdfeb69c8d87fd7d2c10e8d5408851404f7d258cbcea7', '2-ecdbdaea89d75f8e73bde77a46db821cd40f430d39a11c864e5a4868dcb403ed', '3-80ebe2cfef5e40a2cdefef0923ee2bb9d04bc50be5ee308788af98ff609c380a'] | ||
|
||
#### Recovering from shares | ||
|
||
>>> SecretSharer.recover_secret(shares[0:2]) | ||
'c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a' | ||
|
||
### Plaintext Secrets | ||
|
||
#### Splitting into shares | ||
|
||
>>> from pyseltongue import PlaintextToHexSecretSharer | ||
>>> shares = PlaintextToHexSecretSharer.split_secret("correct horse battery staple", 2, 3) | ||
['1-7da6b11af146449675780434f6589230a3435d9ab59910354205996f508b8d0d', '2-fb4d6235e28c892cea70367c15ec3cbfed4cf4a417bd01e9812980f3ac97ddc8', '3-78f41350d3d2cdc35f6868c3357fe74f37568bad79e0f39dc04d687808a42d5a'] | ||
|
||
#### Recovering from shares | ||
|
||
>>> PlaintextToHexSecretSharer.recover_secret(shares[0:2]) | ||
'correct horse battery staple' | ||
|
||
### Bitcoin Private Keys | ||
|
||
Note: Bitcoin private keys are in [Base58 check](https://en.bitcoin.it/wiki/Base58Check_encoding) format. | ||
|
||
#### Splitting into reliably printable base58 shares | ||
|
||
>>> from pyseltongue import BitcoinToB58SecretSharer | ||
>>> shares = BitcoinToB58SecretSharer.split_secret("5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS", 2, 3) | ||
['2-Bqni1ysZcXhFBhVVJLQgPimDUJrjBrzuvBmc6gPNPh1jyDcvM6uYUuH', '3-9xpMBerBCdHLKzCQ82fjVLfZ3Qt48sqa6nz1E3cc6eu3qUe58vaogU3', '4-85qzMKpnnisRUGuJwivnaxZtcWuP5tgEHQCQMQqqocnMhjfDvkG4t2o'] | ||
|
||
#### Recovering from base58 shares | ||
|
||
>>> BitcoinToB58SecretSharer.recover_secret(shares[0:2]) | ||
'5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS' | ||
|
||
#### Splitting into reliably transcribable [base32](http://en.wikipedia.org/wiki/Base32) shares | ||
|
||
>>> from pyseltongue import BitcoinToB32SecretSharer | ||
>>> shares = BitcoinToB32SecretSharer.split_secret("5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS", 2, 3) | ||
['B-RJ6Y56OSUWDY5VAAGC6XLSTM64CAJ2LPBNB7NKATJCWC7VSHIP5DQIVMR6OGJ4GB', 'C-CT5R24XAR5B732JWYQKSYOYBSF5VHI73HLY24QCFRJR5XUW64C4JWYN6SRGWVCUG', 'D-T54KX27OPEAGZ7TNK5WOFK4WFPZKEXUHNKPWLWDXZQNYPT3WPV3P5IGQTD7HAJDG'] | ||
|
||
#### Recovering from base32 shares | ||
|
||
>>> BitcoinToB32SecretSharer.recover_secret(shares[0:2]) | ||
'5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS' | ||
|
||
#### Splitting into reliably transcribable [zbase32](http://philzimmermann.com/docs/human-oriented-base-32-encoding.txt) shares | ||
|
||
>>> from pyseltongue import BitcoinToZB32SecretSharer | ||
>>> shares = BitcoinToZB32SecretSharer.split_secret("5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS", 2, 3) | ||
['b-aweuzkm9jmfgd7x4k595bzcm3er3epf4dprfwzpprqa3exbuocs9byn4owfuqbo', 'n-btetgqqu8doacarsbyfdzpyycyj6gfdeaaxrpfx33pdjk4ou1d5owjdmdi1iegm9', 'd-njh33f14q7smucmh8iq8uaewc8mzub3mzptrwsegfiz3hc1fozkkjtguc4trh6sq'] | ||
|
||
#### Recovering from zbase32 shares | ||
|
||
>>> BitcoinToZB32SecretSharer.recover_secret(shares[0:2]) | ||
'5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS' | ||
|
||
### Raw integers | ||
|
||
#### Splitting into shares | ||
|
||
>>> from pyseltongue import secret_int_to_points, points_to_secret_int | ||
>>> secret = 88985120633792790105905686761572077713049967498756747774697023364147812997770L | ||
>>> shares = secret_int_to_points(secret, 2, 3) | ||
[(1, 108834987130598118322155382953070549297972563210322923466700361825476188819879L), (2, 12892764390087251114834094135881113029625174256248535119246116278891435001755L), (3, 32742630886892579331083790327379584614547769967814710811249454740219810823864L)] | ||
|
||
#### Recovering from shares | ||
|
||
>>> points_to_secret_int(shares[0:2]) | ||
88985120633792790105905686761572077713049967498756747774697023364147812997770L | ||
|
||
### Custom formats | ||
|
||
#### Splitting into shares | ||
|
||
>>> from pyseltongue import SecretSharer, base64_chars | ||
>>> sharer_class = SecretSharer | ||
>>> sharer_class.share_charset = base64_chars | ||
>>> shares = sharer_class.split_secret("c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a", 2, 3) | ||
['B-JpxCTUQ9D+q93JglQM9yRinI2Cyxe92FTBSYa93ppfY', 'C-HAmR0pjHuHwL4rozXnFY05ysIJVqtf3pob1HCMaaZUm', 'D-EXbhV+1SYQ1Z6NxBfBM/YQ+PaP4j8B5N92X1pa9LJJ0'] | ||
|
||
#### Recovering from shares | ||
|
||
>>> sharer_class.recover_secret(shares[0:2]) | ||
'c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
Secret Sharing | ||
~~~~~ | ||
:copyright: (c) 2014 by Halfmoon Labs | ||
:license: MIT, see LICENSE for more details. | ||
""" | ||
name = "nginsecretsharing" | ||
|
||
__version__ = '0.3.0' | ||
|
||
from .sharing import secret_int_to_points, points_to_secret_int, \ | ||
point_to_share_string, share_string_to_point, SecretSharer, \ | ||
HexToHexSecretSharer, PlaintextToHexSecretSharer, \ | ||
BitcoinToB58SecretSharer, BitcoinToB32SecretSharer, \ | ||
BitcoinToZB32SecretSharer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
Secret Sharing | ||
~~~~~ | ||
:copyright: (c) 2014 by Halfmoon Labs | ||
:license: MIT, see LICENSE for more details. | ||
""" | ||
|
||
from utilitybelt import secure_randint as randint | ||
|
||
|
||
def egcd(a, b): | ||
if a == 0: | ||
return (b, 0, 1) | ||
else: | ||
g, y, x = egcd(b % a, a) | ||
return (g, x - (b // a) * y, y) | ||
|
||
|
||
def mod_inverse(k, prime): | ||
k = k % prime | ||
if k < 0: | ||
ret = egcd(prime, -k)[2] | ||
else: | ||
ret = egcd(prime, k)[2] | ||
return (prime + ret) % prime | ||
|
||
|
||
def random_polynomial(degree, intercept, upper_bound): | ||
""" Generates a random polynomial with positive coefficients. | ||
""" | ||
if degree < 0: | ||
raise ValueError('Degree must be a non-negative number.') | ||
coefficients = [intercept] | ||
for i in range(degree): | ||
random_coeff = randint(0, upper_bound-1) | ||
coefficients.append(random_coeff) | ||
return coefficients | ||
|
||
|
||
def get_polynomial_points(coefficients, num_points, prime): | ||
""" Calculates the first n polynomial points. | ||
[ (1, f(1)), (2, f(2)), ... (n, f(n)) ] | ||
""" | ||
points = [] | ||
for x in range(1, num_points+1): | ||
# start with x=1 and calculate the value of y | ||
y = coefficients[0] | ||
# calculate each term and add it to y, using modular math | ||
for i in range(1, len(coefficients)): | ||
exponentiation = (x**i) % prime | ||
term = (coefficients[i] * exponentiation) % prime | ||
y = (y + term) % prime | ||
# add the point to the list of points | ||
points.append((x, y)) | ||
return points | ||
|
||
|
||
def modular_lagrange_interpolation(x, points, prime): | ||
# break the points up into lists of x and y values | ||
x_values, y_values = zip(*points) | ||
# initialize f(x) and begin the calculation: f(x) = SUM( y_i * l_i(x) ) | ||
f_x = 0 | ||
for i in range(len(points)): | ||
# evaluate the lagrange basis polynomial l_i(x) | ||
numerator, denominator = 1, 1 | ||
for j in range(len(points)): | ||
# don't compute a polynomial fraction if i equals j | ||
if i == j: | ||
continue | ||
# compute a fraction & update the existing numerator + denominator | ||
numerator = (numerator * (x - x_values[j])) % prime | ||
denominator = (denominator * (x_values[i] - x_values[j])) % prime | ||
# get the polynomial from the numerator + denominator mod inverse | ||
lagrange_polynomial = numerator * mod_inverse(denominator, prime) | ||
# multiply the current y & the evaluated polynomial & add it to f(x) | ||
f_x = (prime + f_x + (y_values[i] * lagrange_polynomial)) % prime | ||
return f_x |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
Secret Sharing | ||
~~~~~ | ||
:copyright: (c) 2014 by Halfmoon Labs | ||
:license: MIT, see LICENSE for more details. | ||
""" | ||
|
||
|
||
def calculate_mersenne_primes(): | ||
""" Returns all the mersenne primes with less than 500 digits. | ||
All primes: | ||
3, 7, 31, 127, 8191, 131071, 524287, 2147483647L, 2305843009213693951L, | ||
618970019642690137449562111L, 162259276829213363391578010288127L, | ||
170141183460469231731687303715884105727L, | ||
68647976601306097149...12574028291115057151L, (157 digits) | ||
53113799281676709868...70835393219031728127L, (183 digits) | ||
10407932194664399081...20710555703168729087L, (386 digits) | ||
""" | ||
mersenne_prime_exponents = [ | ||
2, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127, 521, 607, 1279 | ||
] | ||
primes = [] | ||
for exp in mersenne_prime_exponents: | ||
prime = 1 | ||
for i in range(exp): | ||
prime *= 2 | ||
prime -= 1 | ||
primes.append(prime) | ||
return primes | ||
|
||
SMALLEST_257BIT_PRIME = (2**256 + 297) | ||
SMALLEST_321BIT_PRIME = (2**320 + 27) | ||
SMALLEST_385BIT_PRIME = (2**384 + 231) | ||
STANDARD_PRIMES = calculate_mersenne_primes() + [ | ||
SMALLEST_257BIT_PRIME, SMALLEST_321BIT_PRIME, SMALLEST_385BIT_PRIME | ||
] | ||
STANDARD_PRIMES.sort() | ||
|
||
|
||
def get_large_enough_prime(batch): | ||
""" Returns a prime number that is greater all the numbers in the batch. | ||
""" | ||
# build a list of primes | ||
primes = STANDARD_PRIMES | ||
# find a prime that is greater than all the numbers in the batch | ||
for prime in primes: | ||
numbers_greater_than_prime = [i for i in batch if i > prime] | ||
# if len(numbers_greater_than_prime) == 0: | ||
if not numbers_greater_than_prime: | ||
return prime | ||
return None |
Oops, something went wrong.