| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											2014-06-23 22:20:10 +02:00
										 |  |  | # SecretSharing.py : distribute a secret amongst a group of participants | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  | # | 
					
						
							|  |  |  | # =================================================================== | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2014-06-23 22:20:10 +02:00
										 |  |  | # Copyright (c) 2014, Legrandin <helderijs@gmail.com> | 
					
						
							|  |  |  | # All rights reserved. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Redistribution and use in source and binary forms, with or without | 
					
						
							|  |  |  | # modification, are permitted provided that the following conditions | 
					
						
							|  |  |  | # are met: | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # 1. Redistributions of source code must retain the above copyright | 
					
						
							|  |  |  | #    notice, this list of conditions and the following disclaimer. | 
					
						
							|  |  |  | # 2. Redistributions in binary form must reproduce the above copyright | 
					
						
							|  |  |  | #    notice, this list of conditions and the following disclaimer in | 
					
						
							|  |  |  | #    the documentation and/or other materials provided with the | 
					
						
							|  |  |  | #    distribution. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
					
						
							|  |  |  | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
					
						
							|  |  |  | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | 
					
						
							|  |  |  | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | 
					
						
							|  |  |  | # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | 
					
						
							|  |  |  | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | 
					
						
							|  |  |  | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 
					
						
							|  |  |  | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 
					
						
							|  |  |  | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
					
						
							|  |  |  | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | 
					
						
							|  |  |  | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | 
					
						
							|  |  |  | # POSSIBILITY OF SUCH DAMAGE. | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  | # =================================================================== | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  | from Crypto.Util.py3compat import is_native_int | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  | from Crypto.Util import number | 
					
						
							|  |  |  | from Crypto.Util.number import long_to_bytes, bytes_to_long | 
					
						
							|  |  |  | from Crypto.Random import get_random_bytes as rng | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  | def _mult_gf2(f1, f2): | 
					
						
							|  |  |  |     """Multiply two polynomials in GF(2)""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Ensure f2 is the smallest | 
					
						
							|  |  |  |     if f2 > f1: | 
					
						
							|  |  |  |         f1, f2 = f2, f1 | 
					
						
							| 
									
										
										
										
											2014-06-11 15:46:22 +02:00
										 |  |  |     z = 0 | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |     while f2: | 
					
						
							|  |  |  |         if f2 & 1: | 
					
						
							|  |  |  |             z ^= f1 | 
					
						
							|  |  |  |         f1 <<= 1 | 
					
						
							|  |  |  |         f2 >>= 1 | 
					
						
							|  |  |  |     return z | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _div_gf2(a, b): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Compute division of polynomials over GF(2). | 
					
						
							|  |  |  |     Given a and b, it finds two polynomials q and r such that: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     a = b*q + r with deg(r)<deg(b) | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (a < b): | 
					
						
							| 
									
										
										
										
											2014-06-11 15:46:22 +02:00
										 |  |  |         return 0, a | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     deg = number.size | 
					
						
							| 
									
										
										
										
											2014-06-11 15:46:22 +02:00
										 |  |  |     q = 0 | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |     r = a | 
					
						
							|  |  |  |     d = deg(b) | 
					
						
							|  |  |  |     while deg(r) >= d: | 
					
						
							| 
									
										
										
										
											2014-06-11 15:46:22 +02:00
										 |  |  |         s = 1 << (deg(r) - d) | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |         q ^= s | 
					
						
							|  |  |  |         r ^= _mult_gf2(b, s) | 
					
						
							|  |  |  |     return (q, r) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _Element(object): | 
					
						
							|  |  |  |     """Element of GF(2^128) field""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # The irreducible polynomial defining this field is 1+x+x^2+x^7+x^128 | 
					
						
							| 
									
										
										
										
											2014-06-11 15:46:22 +02:00
										 |  |  |     irr_poly = 1 + 2 + 4 + 128 + 2 ** 128 | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, encoded_value): | 
					
						
							|  |  |  |         """Initialize the element to a certain value.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The value passed as parameter is internally encoded as | 
					
						
							|  |  |  |         a 128-bit integer, where each bit represents a polynomial | 
					
						
							|  |  |  |         coefficient. The LSB is the constant coefficient. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  |         if is_native_int(encoded_value): | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |             self._value = encoded_value | 
					
						
							|  |  |  |         elif len(encoded_value) == 16: | 
					
						
							|  |  |  |             self._value = bytes_to_long(encoded_value) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise ValueError("The encoded value must be an integer or a 16 byte string") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |     def __eq__(self, other): | 
					
						
							|  |  |  |         return self._value == other._value | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |     def __int__(self): | 
					
						
							|  |  |  |         """Return the field element, encoded as a 128-bit integer.""" | 
					
						
							|  |  |  |         return self._value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def encode(self): | 
					
						
							|  |  |  |         """Return the field element, encoded as a 16 byte string.""" | 
					
						
							|  |  |  |         return long_to_bytes(self._value, 16) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __mul__(self, factor): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f1 = self._value | 
					
						
							|  |  |  |         f2 = factor._value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Make sure that f2 is the smallest, to speed up the loop | 
					
						
							|  |  |  |         if f2 > f1: | 
					
						
							|  |  |  |             f1, f2 = f2, f1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if self.irr_poly in (f1, f2): | 
					
						
							|  |  |  |             return _Element(0) | 
					
						
							| 
									
										
										
										
											2014-06-11 15:46:22 +02:00
										 |  |  |         mask1 = 2 ** 128 | 
					
						
							|  |  |  |         v, z = f1, 0 | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |         while f2: | 
					
						
							|  |  |  |             if f2 & 1: | 
					
						
							|  |  |  |                 z ^= v | 
					
						
							|  |  |  |             v <<= 1 | 
					
						
							|  |  |  |             if v & mask1: | 
					
						
							|  |  |  |                 v ^= self.irr_poly | 
					
						
							|  |  |  |             f2 >>= 1 | 
					
						
							|  |  |  |         return _Element(z) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __add__(self, term): | 
					
						
							|  |  |  |         return _Element(self._value ^ term._value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def inverse(self): | 
					
						
							|  |  |  |         """Return the inverse of this element in GF(2^128).""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # We use the Extended GCD algorithm | 
					
						
							|  |  |  |         # http://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |         if self._value == 0: | 
					
						
							|  |  |  |             raise ValueError("Inversion of zero") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |         r0, r1 = self._value, self.irr_poly | 
					
						
							| 
									
										
										
										
											2014-06-11 15:46:22 +02:00
										 |  |  |         s0, s1 = 1, 0 | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |         while r1 > 0: | 
					
						
							|  |  |  |             q = _div_gf2(r0, r1)[0] | 
					
						
							|  |  |  |             r0, r1 = r1, r0 ^ _mult_gf2(q, r1) | 
					
						
							|  |  |  |             s0, s1 = s1, s0 ^ _mult_gf2(q, s1) | 
					
						
							|  |  |  |         return _Element(s0) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |     def __pow__(self, exponent): | 
					
						
							|  |  |  |         result = _Element(self._value) | 
					
						
							|  |  |  |         for _ in range(exponent - 1): | 
					
						
							|  |  |  |             result = result * self | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Shamir(object): | 
					
						
							|  |  |  |     """Shamir's secret sharing scheme.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |     A secret is split into ``n`` shares, and it is sufficient to collect | 
					
						
							|  |  |  |     ``k`` of them to reconstruct the secret. | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-11 15:46:22 +02:00
										 |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |     def split(k, n, secret, ssss=False): | 
					
						
							|  |  |  |         """Split a secret into ``n`` shares.
 | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |         The secret can be reconstructed later using just ``k`` shares | 
					
						
							|  |  |  |         out of the original ``n``. | 
					
						
							|  |  |  |         Each share must be kept confidential to the person it was | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |         assigned to. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |         Each share is associated to an index (starting from 1). | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 00:09:43 +02:00
										 |  |  |         Args: | 
					
						
							|  |  |  |           k (integer): | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |             The sufficient number of shares to reconstruct the secret (``k < n``). | 
					
						
							| 
									
										
										
										
											2017-08-18 00:09:43 +02:00
										 |  |  |           n (integer): | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |             The number of shares that this method will create. | 
					
						
							| 
									
										
										
										
											2017-08-18 00:09:43 +02:00
										 |  |  |           secret (byte string): | 
					
						
							| 
									
										
										
										
											2020-06-22 00:00:48 +02:00
										 |  |  |             A byte string of 16 bytes (e.g. the AES 128 key). | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |           ssss (bool): | 
					
						
							|  |  |  |             If ``True``, the shares can be used with the ``ssss`` utility. | 
					
						
							|  |  |  |             Default: ``False``. | 
					
						
							| 
									
										
										
										
											2017-08-18 00:09:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |         Return (tuples): | 
					
						
							|  |  |  |             ``n`` tuples. A tuple is meant for each participant and it contains two items: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             1. the unique index (an integer) | 
					
						
							|  |  |  |             2. the share (a byte string, 16 bytes) | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  |         # We create a polynomial with random coefficients in GF(2^128): | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  |         # p(x) = \sum_{i=0}^{k-1} c_i * x^i | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  |         # c_0 is the encoded secret | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  |         coeffs = [_Element(rng(16)) for i in range(k - 1)] | 
					
						
							| 
									
										
										
										
											2020-06-22 00:00:48 +02:00
										 |  |  |         coeffs.append(_Element(secret)) | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Each share is y_i = p(x_i) where x_i is the public index | 
					
						
							|  |  |  |         # associated to each of the n users. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |         def make_share(user, coeffs, ssss): | 
					
						
							| 
									
										
										
										
											2020-06-22 00:00:48 +02:00
										 |  |  |             idx = _Element(user) | 
					
						
							|  |  |  |             share = _Element(0) | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |             for coeff in coeffs: | 
					
						
							| 
									
										
										
										
											2020-06-22 00:00:48 +02:00
										 |  |  |                 share = idx * share + coeff | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |             if ssss: | 
					
						
							|  |  |  |                 share += _Element(user) ** len(coeffs) | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |             return share.encode() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |         return [(i, make_share(i, coeffs, ssss)) for i in range(1, n + 1)] | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-11 15:46:22 +02:00
										 |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |     def combine(shares, ssss=False): | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |         """Recombine a secret, if enough shares are presented.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 00:09:43 +02:00
										 |  |  |         Args: | 
					
						
							|  |  |  |           shares (tuples): | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |             The *k* tuples, each containin the index (an integer) and | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |             the share (a byte string, 16 bytes long) that were assigned to | 
					
						
							|  |  |  |             a participant. | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |           ssss (bool): | 
					
						
							|  |  |  |             If ``True``, the shares were produced by the ``ssss`` utility. | 
					
						
							|  |  |  |             Default: ``False``. | 
					
						
							| 
									
										
										
										
											2017-08-18 00:09:43 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         Return: | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |             The original secret, as a byte string (16 bytes long). | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  |         # Given k points (x,y), the interpolation polynomial of degree k-1 is: | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  |         # L(x) = \sum_{j=0}^{k-1} y_i * l_j(x) | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  |         # where: | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  |         # l_j(x) = \prod_{ \overset{0 \le m \le k-1}{m \ne j} } | 
					
						
							|  |  |  |         #          \frac{x - x_m}{x_j - x_m} | 
					
						
							|  |  |  |         # | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |         # However, in this case we are purely interested in the constant | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |         # coefficient of L(x). | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |         k = len(shares) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         gf_shares = [] | 
					
						
							|  |  |  |         for x in shares: | 
					
						
							|  |  |  |             idx = _Element(x[0]) | 
					
						
							|  |  |  |             value = _Element(x[1]) | 
					
						
							|  |  |  |             if any(y[0] == idx for y in gf_shares): | 
					
						
							|  |  |  |                 raise ValueError("Duplicate share") | 
					
						
							|  |  |  |             if ssss: | 
					
						
							|  |  |  |                 value += idx ** k | 
					
						
							|  |  |  |             gf_shares.append((idx, value)) | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         result = _Element(0) | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  |         for j in range(k): | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |             x_j, y_j = gf_shares[j] | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             coeff_0_l = _Element(0) | 
					
						
							|  |  |  |             while not int(coeff_0_l): | 
					
						
							|  |  |  |                 coeff_0_l = _Element(rng(16)) | 
					
						
							|  |  |  |             inv = coeff_0_l.inverse() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-22 00:00:48 +02:00
										 |  |  |             denominator = _Element(1) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-04 11:31:40 +01:00
										 |  |  |             for m in range(k): | 
					
						
							| 
									
										
										
										
											2020-06-21 23:43:44 +02:00
										 |  |  |                 x_m = gf_shares[m][0] | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |                 if m != j: | 
					
						
							| 
									
										
										
										
											2020-06-22 00:00:48 +02:00
										 |  |  |                     coeff_0_l *= x_m | 
					
						
							|  |  |  |                     denominator *= x_j + x_m | 
					
						
							|  |  |  |             result += y_j * coeff_0_l * denominator.inverse() * inv | 
					
						
							| 
									
										
										
										
											2014-04-27 18:13:15 +02:00
										 |  |  |         return result.encode() |