diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ddce579 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm + +/*.iml + +## Directory-based project format: +.idea/ + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +out/ + +.idea_modules/ + +### Python template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ diff --git a/README b/README index 03eefb3..44700a1 100644 --- a/README +++ b/README @@ -1,4 +1,3 @@ - Python implementation of Shamir's Secret Sharing Scheme. Uses polynomials on GF256. diff --git a/pyssss/GF256elt.py b/pyssss/GF256elt.py index 9b6ae21..775481e 100644 --- a/pyssss/GF256elt.py +++ b/pyssss/GF256elt.py @@ -1,171 +1,179 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- # -# Copyright 2010 Mathias Herberts +# Copyright 2010 Mathias Herberts # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # + class Callable: def __init__(self, anycallable): - self.__call__ = anycallable + self.function = anycallable + + def __call__(self): + return self.function(self) + class GF256elt: - """A class for representing GF256 (GF(2^8)) elements. - Those elements are representations of polynomials over GF(2) with - each bit being the coefficient of x^k for k an integer in [0,7]. - The log/exp tables are generated by generate_logexp_tables or generate_pplogexp_tables.""" - - __logtable = [] - __exptable = [] - - def __init__(self,value): - self.__bytevalue = value % 256 - - def __add__(self,other): - if isinstance(other, GF256elt): - return GF256elt(self.__bytevalue ^ other.__bytevalue) - else: - raise Exception() - - def __sub__(self,other): - "In GF256 (and more generally in GF(2^n)) a + b = a - b so just call __add__" - if isinstance(other, GF256elt): - return self.__add__(other) - else: - raise Exception() - - def __mul__(self,other): - if not isinstance(other,GF256elt): - raise Exception() - - # If one of the terms is 0, return 0 - if self.__bytevalue == 0 or other.__bytevalue == 0: - return GF256elt(0) - - # Extract the log of each term then determine the new power - - p = (self.log() + other.log()) % 255 - - return GF256elt(GF256elt.__exptable[p]) - - def __div__(self,other): - if not isinstance(other,GF256elt): - raise Exception() - - # If second term is 0, raise an exception - if other.__bytevalue == 0: - raise Exception() - - # If first term is 0, return 0 - if self.__bytevalue == 0: - return GF256elt(0) - - # Compute the new power from the logs of the operands - - p = (self.log() - other.log()) % 255 - - if p < 0: - p += 255 - - return GF256elt(GF256elt.__exptable[p]) - - def log(self): - "Compute the power n so that x^n is equivalent to self in GF256." - - if self.__bytevalue == 0: - raise Exception() - - # Use the lookup table for faster computations - return GF256elt.__logtable[self.__bytevalue] - - def __hex__(self): - return "%02x" % self.__bytevalue - - def __int__(self): - return self.__bytevalue - - def __str__(self): - return str(self.__bytevalue) - - def __eq__(self,other): - return self.__bytevalue == other.__bytevalue - - def generate_pplogexp_tables(PP): - """Generate logarithm and exponential tables for Gf(256) with a prime - polynomial whose value is 'PP'.""" - - GF = 256 - - GF256elt.__logtable = [0 for i in range(GF)] - GF256elt.__exptable = [0 for i in range(GF)] - - GF256elt.__logtable[0] = (1 - GF) & 0xff - GF256elt.__exptable[0] = 1 - - for i in xrange(1,GF): - GF256elt.__exptable[i] = GF256elt.__exptable[i-1] * 2 - if GF256elt.__exptable[i] >= GF: - GF256elt.__exptable[i] ^= PP - - GF256elt.__exptable[i] &= 0xff - GF256elt.__logtable[GF256elt.__exptable[i]]= i - - def generate_logexp_tables(): - """Generate logarithm and exponential tables for the GF(256) generator 0x03 - and the modulo polynomial x^8 + x^4 + x^3 + x + 1 (0x11b) as defined - in Rijndael. - This method generates the same tables as Logtable and Alogtable that can be - found in file boxes-ref.dat in the reference implementation of Rijndael v2.2. - - see U{http://www.samiam.org/galois.html}.""" - - GF256elt.__logtable = [0 for i in range(256)] - GF256elt.__exptable = [0 for i in range(256)] - - # First exponential is 0x03 to the 0th power - exp = 1 - - for exponent in range(255): - exp &= 0xff - GF256elt.__exptable[exponent] = exp - # Multiply exp by three - d = exp & 0x80 - exp <<= 1 - - if d == 0x80: - exp ^= 0x1b - - exp ^= GF256elt.__exptable[exponent] - - GF256elt.__logtable[GF256elt.__exptable[exponent]] = exponent - - GF256elt.__exptable[255] = GF256elt.__exptable[0] - GF256elt.__logtable[0] = 0 - - def dump_tables(): - print GF256elt.__exptable - print GF256elt.__logtable - - generate_logexp_tables = Callable(generate_logexp_tables) - generate_pplogexp_tables = Callable(generate_pplogexp_tables) - dump_tables = Callable(dump_tables) - -## -## Generate log/exp tables based on a prime polynomial -## -## Possible values of PP are 285 299 301 333 351 355 357 361 369 391 397 425 451 463 487 501 -## -## + """A class for representing GF256 (GF(2^8)) elements. + Those elements are representations of polynomials over GF(2) with + each bit being the coefficient of x^k for k an integer in [0,7]. + The log/exp tables are generated by generate_logexp_tables or generate_pplogexp_tables.""" + + __logtable = [] + __exptable = [] + + def __init__(self, value): + self.__bytevalue = value % 256 + + def __add__(self, other): + if isinstance(other, GF256elt): + return GF256elt(self.__bytevalue ^ other.__bytevalue) + else: + raise Exception() + + def __sub__(self, other): + "In GF256 (and more generally in GF(2^n)) a + b = a - b so just call __add__" + if isinstance(other, GF256elt): + return self.__add__(other) + else: + raise Exception() + + def __mul__(self, other): + if not isinstance(other, GF256elt): + raise Exception() + + # If one of the terms is 0, return 0 + if self.__bytevalue == 0 or other.__bytevalue == 0: + return GF256elt(0) + + # Extract the log of each term then determine the new power + + p = (self.log() + other.log()) % 255 + + return GF256elt(GF256elt.__exptable[p]) + + def __div__(self, other): + if not isinstance(other, GF256elt): + raise Exception() + + # If second term is 0, raise an exception + if other.__bytevalue == 0: + raise Exception() + + # If first term is 0, return 0 + if self.__bytevalue == 0: + return GF256elt(0) + + # Compute the new power from the logs of the operands + + p = (self.log() - other.log()) % 255 + + if p < 0: + p += 255 + + return GF256elt(GF256elt.__exptable[p]) + + def log(self): + "Compute the power n so that x^n is equivalent to self in GF256." + + if self.__bytevalue == 0: + raise Exception() + + # Use the lookup table for faster computations + return GF256elt.__logtable[self.__bytevalue] + + def __hex__(self): + return "%02x" % self.__bytevalue + + def __int__(self): + return self.__bytevalue + + def __str__(self): + return str(self.__bytevalue) + + def __eq__(self, other): + return self.__bytevalue == other.__bytevalue + + @staticmethod + def generate_pplogexp_tables(prime_polynomial): + """Generate logarithm and exponential tables for Gf(256) with a prime polynomial.""" + + GF = 256 + + GF256elt.__logtable = [0 for i in range(GF)] + GF256elt.__exptable = [0 for i in range(GF)] + + GF256elt.__logtable[0] = (1 - GF) & 0xff + GF256elt.__exptable[0] = 1 + + for i in xrange(1, GF): + GF256elt.__exptable[i] = GF256elt.__exptable[i - 1] * 2 + if GF256elt.__exptable[i] >= GF: + GF256elt.__exptable[i] ^= prime_polynomial + + GF256elt.__exptable[i] &= 0xff + GF256elt.__logtable[GF256elt.__exptable[i]] = i + + @staticmethod + def generate_logexp_tables(): + """Generate logarithm and exponential tables for the GF(256) generator 0x03 + and the modulo polynomial x^8 + x^4 + x^3 + x + 1 (0x11b) as defined + in Rijndael. + This method generates the same tables as Logtable and Alogtable that can be + found in file boxes-ref.dat in the reference implementation of Rijndael v2.2. + + see U{http://www.samiam.org/galois.html}.""" + + GF256elt.__logtable = [0 for i in range(256)] + GF256elt.__exptable = [0 for i in range(256)] + + # First exponential is 0x03 to the 0th power + exp = 1 + + for exponent in range(255): + exp &= 0xff + GF256elt.__exptable[exponent] = exp + # Multiply exp by three + d = exp & 0x80 + exp <<= 1 + + if d == 0x80: + exp ^= 0x1b + + exp ^= GF256elt.__exptable[exponent] + + GF256elt.__logtable[GF256elt.__exptable[exponent]] = exponent + + GF256elt.__exptable[255] = GF256elt.__exptable[0] + GF256elt.__logtable[0] = 0 + + @staticmethod + def dump_tables(): + print GF256elt.__exptable + print GF256elt.__logtable + + generate_logexp_tables = Callable(generate_logexp_tables) + generate_pplogexp_tables = Callable(generate_pplogexp_tables) + dump_tables = Callable(dump_tables) + +# # +# # Generate log/exp tables based on a prime polynomial +# # +# # Possible values of PP are 285 299 301 333 351 355 357 361 369 391 397 425 451 463 487 501 +# # +# # # # For Rijndael compatibility (0x11b prime polynomial and 0x03 as generator) @@ -175,5 +183,5 @@ def dump_tables(): # # For buttsoft/QR Code compatibility (0x11d prime polynomial) # -#GF256elt.generate_pplogexp_tables(0x11d) +# GF256elt.generate_pplogexp_tables(0x11d) diff --git a/pyssss/PGF256.py b/pyssss/PGF256.py index a98baa4..040b3b2 100644 --- a/pyssss/PGF256.py +++ b/pyssss/PGF256.py @@ -1,146 +1,147 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- # -# Copyright 2010 Mathias Herberts +# Copyright 2010 Mathias Herberts # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # from GF256elt import GF256elt + class PGF256: - """Class for representing polynomials whose coefficients are GF256elt.""" - - def __init__(self,coeffs): - # Polynomial coefficients, element i is the coefficient for x^i - self.__coefficients = [] - - for coeff in coeffs: - if not isinstance(coeff, GF256elt): - raise Exception() - self.__coefficients.append(coeff) - - def __add__(self,other): - if isinstance(other, GF256elt): - c = self.coeffs() - c[0] += other - return PGF256(c) - - if not isinstance(other,PGF256): - raise Exception() - - minDeg = min(self.deg(),other.deg()) - maxDeg = max(self.deg(),other.deg()) - - coeffs = [] - - for i in xrange(0,minDeg): - coeffs.append(self.coeff(i) + other.coeff(i)) - - for i in xrange(minDeg,maxDeg): - if self.deg() > other.deg(): - coeffs.append(self.coeff(i)) - else: - coeffs.append(other.coeff(i)) - - return PGF256(coeffs) - - def __sub__(self,other): - if isinstance(other, GF256elt): - c = self.coeffs() - c[0] -= other - return PGF256(c) - - if not isinstance(other,PGF256): - raise Exception() - - minDeg = min(self.deg(),other.deg()) - maxDeg = max(self.deg(),other.deg()) - - coeffs = [] - - for i in xrange(0,minDeg + 1): - coeffs.append(self.coeff(i) - other.coeff(i)) - - zero = GF256elt(0) - - for i in xrange(minDeg + 1,maxDeg + 1): - if self.deg() > other.deg(): - coeffs.append(self.coeff(i)) - else: - coeffs.append(zero - other.coeff(i)) - - return PGF256(coeffs) - - def __mul__(self,other): - if isinstance(other, GF256elt): - c = self.coeffs() - return PGF256(map(lambda x: x * other,c)) - - if not isinstance(other,PGF256): - raise Exception() - - rescoeffs = [GF256elt(0) for i in xrange(0,self.deg() + other.deg() + 1)] - - for i in xrange(0,self.deg() + 1): - for j in xrange(0,other.deg() + 1): - rescoeffs[i + j] += self.coeff(i) * other.coeff(j) - - return PGF256(rescoeffs) - - def coeff(self,i): - "Return the coefficient for x^i." - - if i >= len(self.__coefficients): - return GF256elt(0) - else: - return self.__coefficients[i] - - def coeffs(self): - "Return a clone of the array of coefficients" - - c = [] - - zero = GF256elt(0) - - for i in xrange(0,self.deg() + 1): - c.append(self.coeff(i) + zero) - - return c - - def __repr__(self): - - for i in range(0,len(self.__coefficients)): - if i == 0: - p = str(self.coeff(i)) - elif str(self.coeff(i)) != "0": - p = str(self.coeff(i)) +"*x^"+str(i)+" + " + p - - return p - - def deg(self): - "Return the degree of this polynomial." - return len(self.__coefficients) - 1 - - def f(self,x): - "Compute f(x) where f is the current polynomial. We use Horner's scheme for faster result." - - if not isinstance(x, GF256elt): - raise Exception() - - result = GF256elt(0) - - for i in range(1,len(self.__coefficients)+1): - result = result * x - result += self.__coefficients[len(self.__coefficients) - i] - - return result + """Class for representing polynomials whose coefficients are GF256elt.""" + + def __init__(self, coeffs): + # Polynomial coefficients, element i is the coefficient for x^i + self.__coefficients = [] + + for coeff in coeffs: + if not isinstance(coeff, GF256elt): + raise Exception() + self.__coefficients.append(coeff) + + def __add__(self, other): + if isinstance(other, GF256elt): + c = self.coeffs() + c[0] += other + return PGF256(c) + + if not isinstance(other, PGF256): + raise Exception() + + minDeg = min(self.deg(), other.deg()) + maxDeg = max(self.deg(), other.deg()) + + coeffs = [] + + for i in xrange(0, minDeg): + coeffs.append(self.coeff(i) + other.coeff(i)) + + for i in xrange(minDeg, maxDeg): + if self.deg() > other.deg(): + coeffs.append(self.coeff(i)) + else: + coeffs.append(other.coeff(i)) + + return PGF256(coeffs) + + def __sub__(self, other): + if isinstance(other, GF256elt): + c = self.coeffs() + c[0] -= other + return PGF256(c) + + if not isinstance(other, PGF256): + raise Exception() + + minDeg = min(self.deg(), other.deg()) + maxDeg = max(self.deg(), other.deg()) + + coeffs = [] + + for i in xrange(0, minDeg + 1): + coeffs.append(self.coeff(i) - other.coeff(i)) + + zero = GF256elt(0) + + for i in xrange(minDeg + 1, maxDeg + 1): + if self.deg() > other.deg(): + coeffs.append(self.coeff(i)) + else: + coeffs.append(zero - other.coeff(i)) + + return PGF256(coeffs) + + def __mul__(self, other): + if isinstance(other, GF256elt): + c = self.coeffs() + return PGF256(map(lambda x: x * other, c)) + + if not isinstance(other, PGF256): + raise Exception() + + rescoeffs = [GF256elt(0) for i in xrange(0, self.deg() + other.deg() + 1)] + + for i in xrange(0, self.deg() + 1): + for j in xrange(0, other.deg() + 1): + rescoeffs[i + j] += self.coeff(i) * other.coeff(j) + + return PGF256(rescoeffs) + + def coeff(self, i): + """Return the coefficient for x^i.""" + + if i >= len(self.__coefficients): + return GF256elt(0) + else: + return self.__coefficients[i] + + def coeffs(self): + """Return a clone of the array of coefficients""" + + c = [] + + zero = GF256elt(0) + + for i in xrange(0, self.deg() + 1): + c.append(self.coeff(i) + zero) + + return c + + def __repr__(self): + p = '' + for i in xrange(0, len(self.__coefficients)): + if i == 0: + p = str(self.coeff(i)) + elif str(self.coeff(i)) != "0": + p = '{coeff}*x^{i}{p}'.format(coeff=self.coeff(i), i=i, p=p) + return p + + def deg(self): + """Return the degree of this polynomial.""" + return len(self.__coefficients) - 1 + + def f(self, x): + """Compute f(x) where f is the current polynomial. We use Horner's scheme for faster result.""" + + if not isinstance(x, GF256elt): + raise Exception() + + result = GF256elt(0) + + for i in range(1, len(self.__coefficients) + 1): + result *= x + result += self.__coefficients[len(self.__coefficients) - i] + + return result diff --git a/pyssss/PGF256Interpolator.py b/pyssss/PGF256Interpolator.py index d9f9dc0..da692c3 100644 --- a/pyssss/PGF256Interpolator.py +++ b/pyssss/PGF256Interpolator.py @@ -1,76 +1,78 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- # -# Copyright 2010 Mathias Herberts +# Copyright 2010 Mathias Herberts # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # from GF256elt import GF256elt from PGF256 import PGF256 + class PGF256Interpolator: - """Lagrange Polynomial Interpolator. - see http://en.wikipedia.org/wiki/Lagrange_polynomial""" - - def interpolate(self,points): - """Returns a PGF256 polynomial interpolating all GF256xGF256 tuples in points.""" - - # - # Check that all points have different X - # - - for i in xrange(0,len(points)): - if points[i][0] in map(lambda x: x[0],points[i+1:]): - raise Exception("Duplicate point exception") - - # - # Special case for k=2 - # - - if (2 == len(points)): - x = PGF256((GF256elt(0),GF256elt(1))) - P = PGF256([points[1][1]]) * (x - points[0][0]) * (GF256elt(1) / (points[1][0] - points[0][0])) - P = P + PGF256([points[0][1]]) * (x - points[1][0]) * (GF256elt(1) / (points[0][0] - points[1][0])); - return P - - # - # Build the interpolating polynomial - # - # L(x) = sigma(j=0,j <= k,yj * Lj(x)) - # Where k = len(points) - 1 - # and Lj(x) = pi(i=0,i <= k and i != j, (x-xi)/(xj - xi)) - # Lj(xi) = kronecker_delta(i,j) - # - - result = PGF256([GF256elt(0)]) - - for j in xrange(0,len(points)): - result = result + (self.__Lj(points,j) * points[j][1]) - - return result - - def __Lj(self,points,j): - result = GF256elt(1) - x = PGF256((GF256elt(0),GF256elt(1))) - for i in xrange(0,len(points)): - if j == i: - continue - - # P = x - P = x - - P = (P - points[i][0]) * (GF256elt(1) / (points[j][0] - points[i][0])) - - result = P * result - - return result + """Lagrange Polynomial Interpolator. + see http://en.wikipedia.org/wiki/Lagrange_polynomial""" + + def interpolate(self, points): + """Returns a PGF256 polynomial interpolating all GF256xGF256 tuples in points.""" + + # + # Check that all points have different X + # + + for i in xrange(0, len(points)): + if points[i][0] in map(lambda x: x[0], points[i + 1:]): + raise Exception("Duplicate point exception") + + # + # Special case for k=2 + # + + if 2 == len(points): + x = PGF256((GF256elt(0), GF256elt(1))) + P = PGF256([points[1][1]]) * (x - points[0][0]) * (GF256elt(1) / (points[1][0] - points[0][0])) + P = P + PGF256([points[0][1]]) * (x - points[1][0]) * (GF256elt(1) / (points[0][0] - points[1][0])) + return P + + # + # Build the interpolating polynomial + # + # L(x) = sigma(j=0,j <= k,yj * Lj(x)) + # Where k = len(points) - 1 + # and Lj(x) = pi(i=0,i <= k and i != j, (x-xi)/(xj - xi)) + # Lj(xi) = kronecker_delta(i,j) + # + + result = PGF256([GF256elt(0)]) + + for j in xrange(0, len(points)): + result = result + (self.__Lj(points, j) * points[j][1]) + + return result + + def __Lj(self, points, j): + result = GF256elt(1) + x = PGF256((GF256elt(0), GF256elt(1))) + for i in xrange(0, len(points)): + if j == i: + continue + + # P = x + P = x + + P = (P - points[i][0]) * (GF256elt(1) / (points[j][0] - points[i][0])) + + result = P * result + + return result diff --git a/pyssss/PySSSS.py b/pyssss/PySSSS.py index 14d17aa..9a17cb7 100644 --- a/pyssss/PySSSS.py +++ b/pyssss/PySSSS.py @@ -1,168 +1,173 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- # -# Copyright 2010 Mathias Herberts -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Copyright 2010 Mathias Herberts # -# http://www.apache.org/licenses/LICENSE-2.0 +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# http://www.apache.org/licenses/LICENSE-2.0 # -# -*- coding: utf-8 -*- +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import random +from StringIO import StringIO from GF256elt import GF256elt from PGF256 import PGF256 from PGF256Interpolator import PGF256Interpolator -def pickRandomPolynomial(degree,zero): - """Pick a random PGF256 polynomial P such that P(0) = zero""" - - coeffs = [] - - # Set f(0) - coeffs.append(zero) - - # Pick coefficients for x^n with n < degree - - for c in xrange(1,degree): - coeffs.append(GF256elt(random.randint(0,255))) - - # Pick non null coefficient for x^degree - - coeffs.append(GF256elt(random.randint(1,255))) - - return PGF256(coeffs) - - -def encodeByte(byte,n,k): - # Allocate array to track duplicates - picked = [False for i in xrange(0,256)] - - # Pick a random polynomial - P = pickRandomPolynomial(k-1,GF256elt(byte)) - - # Generate the keys - keys = ["" for i in xrange(0,n)] - - for i in xrange(0,n): - - # - # Pick a not yet picked X value in [0,255], - # we need a value in [1,255] but to have a credible entropy for bytes we pick it in [0,255] - # and simply output garbage if we picked 0 - # If we do not do that then the output keys will NEVER have 00 in even positions (starting at 0) which would be a little suspicious for some random data - # - - pick = random.randint(1,255) - - while picked[pick] or pick == 0: - # 0 values will be discarded but output it anyway with trailing garbage - if pick == 0: - keys[i] += chr(0) - keys[i] += chr(random.randint(0,255)) - - pick = random.randint(1,255) - - # Keep track of the value we just picked - picked[pick] = True - - X = GF256elt(pick) - Y = P.f(X) - - keys[i] += chr(int(X)) - keys[i] += chr(int(Y)) - - return keys - -def encode(data,outputs,k): - - n = len(outputs) - - # Loop through the chars - while True: - char = data.read(1) - if 0 == len(char): - break - byte = ord(char) - - charkeys = encodeByte(byte,n,k) - - for i in xrange(0,n): - outputs[i].write(charkeys[i]) - -def decode(keys,output): - - interpolator = PGF256Interpolator() - zero = GF256elt(0) - - data = "" - - - # End Of Key - eok = False - - while not eok: - points = [] - for i in xrange(0,len(keys)): - while True: - b = keys[i].read(1) - if 0 == len(b): - eok = True - break - # Skip points with X value of 0, they were added to respect the entropy of the output - X = ord(b) - if 0 == X: - keys[i].seek(keys[i].tell() + 1) - else: - break - - if eok: - break - - # Extract X/Y - Y = ord(keys[i].read(1)) - - # Push point - points.append((GF256elt(X),GF256elt(Y))) - - if eok: - if 0 != i: - raise Exception('Unexpected EOF while reading key %d' % i) - break - - # Decode next byte - byte = interpolator.interpolate(points).f(zero) - output.write(chr(byte)) -if __name__ == "__main__": - import StringIO - input = StringIO.StringIO("Too many secrets, Marty!") - outputs = [] - n = 5 - k = 3 - for i in xrange(n): - outputs.append(StringIO.StringIO()) +def pickRandomPolynomial(degree, zero): + """Pick a random PGF256 polynomial P such that P(0) = zero""" + + # Set f(0) + coeffs = [zero] + + # Pick coefficients for x^n with n < degree + + for c in xrange(1, degree): + coeffs.append(GF256elt(random.randint(0, 255))) + + # Pick non null coefficient for x^degree + + coeffs.append(GF256elt(random.randint(1, 255))) + + return PGF256(coeffs) + + +def encodeByte(byte, n, k): + # Allocate array to track duplicates + picked = [False for j in xrange(0, 256)] + + # Pick a random polynomial + P = pickRandomPolynomial(k - 1, GF256elt(byte)) + + # Generate the keys + keys = ["" for j in xrange(0, n)] + + for j in xrange(0, n): + + # + # Pick a not yet picked X value in [0,255], + # we need a value in [1,255] but to have a credible entropy for bytes we pick it in [0,255] + # and simply output garbage if we picked 0 + # If we do not do that then the output keys will NEVER have 00 in even positions (starting at 0) + # which would be a little suspicious for some random data + # + + pick = random.randint(1, 255) + + while picked[pick] or pick == 0: + # 0 values will be discarded but output it anyway with trailing garbage + if pick == 0: + keys[j] += chr(0) + keys[j] += chr(random.randint(0, 255)) + + pick = random.randint(1, 255) + + # Keep track of the value we just picked + picked[pick] = True + + X = GF256elt(pick) + Y = P.f(X) + + keys[j] += chr(int(X)) + keys[j] += chr(int(Y)) - encode(input,outputs,k) + return keys - for i in xrange(n): - print outputs[i].getvalue().encode('hex') - inputs = [] - for i in xrange(k): - inputs.append(outputs[i+1]) +def encode(data, outputs, k): + n = len(outputs) - for i in xrange(k): - inputs[i].seek(0) + # Loop through the chars + while True: + char = data.read(1) + if 0 == len(char): + break + byte = ord(char) - output = StringIO.StringIO() - decode(inputs,output) - print output.getvalue() + charkeys = encodeByte(byte, n, k) + + for i in xrange(0, n): + outputs[i].write(charkeys[i]) + + +def decode(keys, output): + interpolator = PGF256Interpolator() + zero = GF256elt(0) + + data = "" + + + # End Of Key + eok = False + + while not eok: + points = [] + for i in xrange(0, len(keys)): + X = 0 + while True: + b = keys[i].read(1) + if 0 == len(b): + eok = True + break + # Skip points with X value of 0, they were added to respect the entropy of the output + X = ord(b) + if 0 == X: + keys[i].seek(keys[i].tell() + 1) + else: + break + + if eok: + break + + # Extract X/Y + Y = ord(keys[i].read(1)) + + # Push point + points.append((GF256elt(X), GF256elt(Y))) + + if eok: + if 0 != i: + raise Exception('Unexpected EOF while reading key %d' % i) + break + + # Decode next byte + byte = interpolator.interpolate(points).f(zero) + output.write(chr(byte)) + + +def main(): + input_string_io = StringIO("Too many secrets, Marty!") + outputs = [] + n = 5 + k = 3 + for i in xrange(n): + outputs.append(StringIO()) + + encode(input_string_io, outputs, k) + + for i in xrange(n): + print outputs[i].getvalue().encode('hex') + + inputs = [] + for i in xrange(k): + inputs.append(outputs[i + 1]) + + for i in xrange(k): + inputs[i].seek(0) + + output = StringIO() + decode(inputs, output) + print output.getvalue() + + +if __name__ == "__main__": + main() diff --git a/pyssss/__init__.py b/pyssss/__init__.py index f3affef..dc174df 100644 --- a/pyssss/__init__.py +++ b/pyssss/__init__.py @@ -14,3 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # + +__version__ = '0.8.0' +__author__ = 'Mathias Herberts' diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..5ab6d43 --- /dev/null +++ b/setup.py @@ -0,0 +1,14 @@ +from setuptools import setup, find_packages +from pyssss import __version__, __author__ + +if __name__ == '__main__': + project_name = "full_text_rss" + setup( + name=project_name, + version=__version__, + author=__author__, + license="Apache Software License", + url="https://github.com/hbs/PySSSS", + packages=find_packages(), + long_description=open('README').read(), + )