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

Bugfix, pytest, better test coverage, code cleanups #24

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@ nosetests.xml
.project
.pydevproject

ignore
# pycharm
.idea/
.cache/
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ language: python
python:
- "2.7"

script: python unit_tests.py
script: pytest
3 changes: 2 additions & 1 deletion AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ Development Leads
Patches and Suggestions
````````````````

- Michael Flaxman (https://github.com/mflaxman)
- Michael Flaxman (https://github.com/mflaxman)
- Michael Loeffler (https://github.com/ml31415)
11 changes: 1 addition & 10 deletions secretsharing/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
# -*- coding: utf-8 -*-
"""
Secret Sharing
~~~~~

:copyright: (c) 2014 by Halfmoon Labs
:license: MIT, see LICENSE for more details.
"""

__version__ = '0.2.7'
__version__ = '0.2.8'

from .sharing import secret_int_to_points, points_to_secret_int, \
point_to_share_string, share_string_to_point, SecretSharer, \
Expand Down
62 changes: 62 additions & 0 deletions secretsharing/charset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from six import integer_types


def int_to_charset(x, charset):
""" Turn a non-negative integer into a string.
"""
if not (isinstance(x, integer_types) and x >= 0):
raise ValueError("x must be a non-negative integer.")
if x == 0:
return charset[0]
output = ""
while x > 0:
x, digit = divmod(x, len(charset))
output += charset[digit]
return output


def charset_to_int(s, charset):
""" Turn a string into a non-negative integer.
"""
if not isinstance(s, (str)):
raise ValueError("s must be a string.")
if (set(s) - set(charset)):
raise ValueError("s has chars that aren't in the charset.")
return sum(len(charset)**i * charset.index(char) for i, char in enumerate(s))



""" Base16 includes numeric digits and the letters a through f. Here,
we use the lowecase letters.
"""
base16_chars = '0123456789abcdef'

""" The Base58 character set allows for strings that avoid visual ambiguity
when typed. It consists of all the alphanumeric characters except for
"0", "O", "I", and "l", which look similar in some fonts.
https://en.bitcoin.it/wiki/Base58Check_encoding
"""
base58_chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"

""" The Base32 character set allows for accurate transcribing by hand.
It consists of uppercase letters + numerals, excluding "0", "1", + "8",
which could look similar to "O", "I", and "B" and so are omitted.
http://en.wikipedia.org/wiki/Base32
"""
base32_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"

""" The z-base-32 character set is similar to the standard Base32 character
set, except it uses lowercase letters + numerals and chooses to exclude
"0", "l", "v", + "2". The set is also permuted so that easier chars
occur more frequently.
http://philzimmermann.com/docs/human-oriented-base-32-encoding.txt
"""
zbase32_chars = "ybndrfg8ejkmcpqxot1uwisza345h769"

""" The Base64 character set is a popular encoding for transmitting data
over media that are designed for textual data. It includes all alphanumeric
characters plus two bonus characters, usually "+" and "/".
http://en.wikipedia.org/wiki/Base64
"""
base64_chars = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789+/")
17 changes: 9 additions & 8 deletions secretsharing/polynomials.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# -*- coding: utf-8 -*-
"""
Secret Sharing
~~~~~
import random

:copyright: (c) 2014 by Halfmoon Labs
:license: MIT, see LICENSE for more details.
"""

from utilitybelt import secure_randint as randint
def randint(min_value, max_value, system_random=None):
""" Return a random integer N such that a <= N <= b.
Uses SystemRandom for generating random numbers.
(which uses os.urandom(), which pulls from /dev/urandom)
"""
if not system_random:
system_random = random.SystemRandom()
return system_random.randint(min_value, max_value)


def egcd(a, b):
Expand Down
52 changes: 0 additions & 52 deletions secretsharing/primes.py

This file was deleted.

56 changes: 33 additions & 23 deletions secretsharing/sharing.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
# -*- coding: utf-8 -*-
"""
Secret Sharing
~~~~~
import string
from six import integer_types

:copyright: (c) 2014 by Halfmoon Labs
:license: MIT, see LICENSE for more details.
"""
from .charset import (int_to_charset, charset_to_int, base58_chars,
base32_chars, zbase32_chars)
from .polynomials import (random_polynomial, get_polynomial_points,
modular_lagrange_interpolation)

import string

from six import integer_types
from utilitybelt import int_to_charset, charset_to_int, base58_chars, \
base32_chars, zbase32_chars
from .primes import get_large_enough_prime
from .polynomials import random_polynomial, \
get_polynomial_points, modular_lagrange_interpolation
def mersenne_primes():
""" Return mersenne primes:
3, 7, 31, 127, 8191, 131071, 524287, 2147483647L, 2305843009213693951L, ...
"""
exponents = [
2, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127, 521, 607, 1279, 2203, 2281,
3217, 4253, 4423, 9689, 9941, 11213, 19937, 21701, 23209, 44497, 86243,
110503, 132049, 216091, 756839, 859433, 1257787, 1398269, 2976221, 3021377,
6972593, 13466917, 20996011, 24036583, 25964951, 30402457, 32582657, 37156667,
42643801, 43112609, 57885161, 74207281
]
for exp in exponents:
yield 2**exp - 1


def get_large_enough_prime(batch):
""" Returns a prime number that is greater all the numbers in the batch.
"""
# find a prime that is greater than all the numbers in the batch
for prime in mersenne_primes():
numbers_greater_than_prime = sum(1 for i in batch if i > prime)
if not numbers_greater_than_prime:
return prime


def secret_int_to_points(secret_int, point_threshold, num_points, prime=None):
Expand Down Expand Up @@ -100,8 +115,8 @@ class SecretSharer():
character set of the secrets and the character set of the shares that
it expects to be dealing with.
"""
secret_charset = string.hexdigits[0:16]
share_charset = string.hexdigits[0:16]
secret_charset = string.hexdigits[:16]
share_charset = string.hexdigits[:16]

def __init__(self):
pass
Expand All @@ -128,15 +143,12 @@ def recover_secret(cls, shares):
class HexToHexSecretSharer(SecretSharer):
""" Standard sharer for converting hex secrets to hex shares.
"""
secret_charset = string.hexdigits[0:16]
share_charset = string.hexdigits[0:16]


class PlaintextToHexSecretSharer(SecretSharer):
""" Good for converting secret messages into standard hex shares.
"""
secret_charset = string.printable
share_charset = string.hexdigits[0:16]


class BitcoinToB58SecretSharer(SecretSharer):
Expand All @@ -147,17 +159,15 @@ class BitcoinToB58SecretSharer(SecretSharer):
share_charset = base58_chars


class BitcoinToB32SecretSharer(SecretSharer):
class BitcoinToB32SecretSharer(BitcoinToB58SecretSharer):
""" Good for converting Bitcoin secret keys into shares that can be
reliably and conveniently transcribed.
"""
secret_charset = base58_chars
share_charset = base32_chars


class BitcoinToZB32SecretSharer(SecretSharer):
class BitcoinToZB32SecretSharer(BitcoinToB58SecretSharer):
""" Good for converting Bitcoin secret keys into shares that can be
reliably and conveniently transcribed.
"""
secret_charset = base58_chars
share_charset = zbase32_chars
Empty file added secretsharing/tests/__init__.py
Empty file.
63 changes: 63 additions & 0 deletions secretsharing/tests/test_sharing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import random
import pytest

from ..charset import base32_chars, base58_chars, base64_chars, int_to_charset, charset_to_int
from .. import (secret_int_to_points, points_to_secret_int,
point_to_share_string, share_string_to_point, SecretSharer,
HexToHexSecretSharer, PlaintextToHexSecretSharer,
BitcoinToB58SecretSharer, BitcoinToB32SecretSharer,
BitcoinToZB32SecretSharer)


@pytest.fixture(params=[BitcoinToB32SecretSharer, BitcoinToB58SecretSharer,
BitcoinToZB32SecretSharer])
def btc_sharer_class(request):
return request.param

splits = [(2, 2), (2, 3), (4, 7), (5, 9)]

def split_and_recover_secret(sharer_class, m, n, secret):
shares = sharer_class.split_secret(secret, m, n)
random.shuffle(shares)
for i in range(n - m):
recovered_secret = sharer_class.recover_secret(shares[i:m+i])
assert(recovered_secret == secret)
recovered_secret = sharer_class.recover_secret(shares[i:m+i+1])
assert(recovered_secret == secret)
unrecovered_secret = sharer_class.recover_secret(shares[i:m+i-1])
assert(unrecovered_secret != secret)


@pytest.mark.parametrize('charset', [base32_chars, base58_chars, base64_chars],
ids=lambda x: 'Base' + str(len(x)))
def test_int_to_charset(charset):
for i in range(30, 100, 4):
secret_int = 2**i + 3*i + 7
secret_str = int_to_charset(secret_int, charset)
assert secret_int == charset_to_int(secret_str, charset)


@pytest.mark.parametrize(['m', 'n'], splits)
def test_hex_to_hex_sharing(m, n):
split_and_recover_secret(SecretSharer, m, n,
"c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a")


@pytest.mark.parametrize(['m', 'n'], splits)
def test_printable_ascii_to_hex_sharing(m, n):
split_and_recover_secret(PlaintextToHexSecretSharer, m, n,
"0000correct horse battery staple")


@pytest.mark.parametrize(['m', 'n'], splits)
def test_btc_sharing(btc_sharer_class, m, n):
split_and_recover_secret(btc_sharer_class, m, n,
"5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS")


@pytest.mark.parametrize(['m', 'n'], splits)
def test_hex_to_base64_sharing(m, n):
sharer_class = SecretSharer
sharer_class.share_charset = base64_chars
split_and_recover_secret(sharer_class, m, n,
"c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a")
3 changes: 1 addition & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

setup(
name='secretsharing',
version='0.2.6',
version='0.2.8',
url='https://github.com/onenameio/secret-sharing',
license='MIT',
author='Halfmoon Labs',
Expand All @@ -21,7 +21,6 @@
zip_safe=False,
install_requires=[
'six',
'utilitybelt',
],
classifiers=[
'Intended Audience :: Developers',
Expand Down
Loading