@@ -122,7 +122,7 @@ def init(bits=None, rngType=None):
122122 # Construct the exp and log tables for multiplication.
123123 primitive = settings .primitive_polynomials [settings .bits ]
124124
125- temp_logs = {} # Temporary Dict to hold logs
125+ temp_logs = {} # Temporary Dict to hold indexed logs
126126 for i in range (settings .size ):
127127 # this works with loop below
128128 exps .insert (i , x )
@@ -229,30 +229,120 @@ def splitNumStringToIntArray(string, pad_length=None):
229229 string = string [::- 1 ]
230230
231231 for i in range (0 , len (string ), settings .bits ):
232- print (string [i : i + settings .bits ][::- 1 ])
232+ # print(string[i : i + settings.bits][::-1])
233233 parts .append (int (string [i : i + settings .bits ][::- 1 ], 2 ))
234234
235235 return parts
236236
237237
238238def horner (x , coeffs ):
239+ if x not in settings .logs :
240+ raise ValueError (f"Value of 'x' ({ x } ) is not found in logs." )
241+
239242 logx = settings .logs [x ]
240243 fx = 0
241244
242245 for i in range (len (coeffs ) - 1 , - 1 , - 1 ):
243246 if fx != 0 :
244- fx = (
245- settings .exps [(logx + settings .logs [fx ]) % settings .maxShares ]
246- ^ coeffs [i ]
247- )
247+
248+ try :
249+
250+ fx = (
251+ settings .exps [
252+ (logx + settings .logs [fx ]) % settings .maxShares
253+ ]
254+ ^ coeffs [i ]
255+ )
256+
257+ except IndexError :
258+ raise IndexError (
259+ f"list index out of range: x:{ x } i:{ i } fx:{ fx } "
260+ )
261+
248262 else :
249263 fx = coeffs [i ]
250264
251265 return fx
252266
253267
254- # lambda bits: bin(1+random.getrandbits(bits))[2:].zfill(bits)
255- # lambda bits: bin(1+secrets.randbits(bits))[2:].zfill(bits)
268+ def getShares (secret , num_shares , threshold ):
269+ shares = []
270+ coeffs = [secret ]
271+
272+ rng = getRNG ()
273+
274+ for i in range (1 , threshold ):
275+
276+ # Generate non-zero random number
277+ random_number = rng (settings .bits )
278+ while int (random_number , 2 ) < 0 :
279+ random_number = rng (settings .bits )
280+
281+ # Check if dithering function is specified and callable, this captures
282+ # the random data using the provided function for dithering or similar
283+ # auditing purposes.
284+ if settings .dithering and callable (settings .dithering ):
285+ capture = settings .dithering
286+ capture (random_number )
287+
288+ coeffs .append (int (random_number , 2 ))
289+
290+ for i in range (1 , num_shares + 1 ):
291+ shares .append ({"x" : i , "y" : horner (i , coeffs )})
292+
293+ return shares
294+
295+
296+ def constructPublicShareString (bits , share_id , data ):
297+ share_id = int (share_id , settings .radix )
298+ bits = bits or settings .bits
299+ bits_base36 = base36encode (bits ).upper ()
300+ id_max = 2 ** bits - 1
301+ id_padding_len = len (hex (int (id_max ))[2 :])
302+ id_hex = padLeft (hex (int (share_id ))[2 :], id_padding_len )
303+
304+ if not (
305+ isinstance (share_id , int )
306+ and share_id % 1 == 0
307+ and 1 <= share_id <= id_max
308+ ):
309+ raise ValueError (
310+ f"Share id must be an integer between 1 and { id_max } , inclusive."
311+ )
312+
313+ new_share_string = bits_base36 + id_hex + data
314+
315+ return new_share_string
316+
317+
318+ def base36encode (number , alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" ):
319+ """Converts an integer to a base36 string."""
320+ if not isinstance (number , int ):
321+ raise TypeError ("number must be an integer" )
322+
323+ base36 = ""
324+ sign = ""
325+
326+ if number < 0 :
327+ sign = "-"
328+ number = - number
329+
330+ if 0 <= number < len (alphabet ):
331+ return sign + alphabet [number ]
332+
333+ while number != 0 :
334+ number , i = divmod (number , len (alphabet ))
335+ base36 = alphabet [i ] + base36
336+
337+ return sign + base36
338+
339+
340+ def base36decode (number ):
341+ return int (number , 36 )
342+
343+
344+ # lambda bits: bin(random.getrandbits(bits))[2:].zfill(bits)
345+ # lambda bits: bin(secrets.randbits(bits))[2:].zfill(bits)
256346# lambda bits: (bin(123456789)[2:].zfill(32) * -(-bits // 32))[-bits:]
257347def setRNG (new_rng = None ):
258348 defaults = settings .get_defaults ()
@@ -357,6 +447,67 @@ def random(bits):
357447 return bin2hex (rng (bits ))
358448
359449
450+ def share (secret , num_shares , threshold , pad_length = None ):
451+ # Security: pad in multiples of 128 bits by default
452+ pad_length = pad_length or 128
453+
454+ if not isinstance (secret , str ):
455+ raise ValueError ("Secret must be a string." )
456+
457+ if not isinstance (num_shares , int ) or num_shares < 2 :
458+ raise ValueError ("Number of shares must be an integer >= 2." )
459+
460+ if num_shares > settings .maxShares :
461+ needed_bits = math .ceil (math .log (num_shares + 1 ) / math .log (2 ))
462+ raise ValueError (
463+ f"Number of shares must be <= { settings .settings .maxShares } ."
464+ f" Use at least { needed_bits } bits."
465+ )
466+
467+ if not isinstance (threshold , int ) or threshold < 2 :
468+ raise ValueError ("Threshold number of shares must be an integer >= 2." )
469+
470+ if threshold > settings .maxShares :
471+ needed_bits = math .ceil (math .log (threshold + 1 ) / math .log (2 ))
472+ raise ValueError (
473+ f"Threshold number of shares must be <= "
474+ f"{ settings .settings .maxShares } . Use at least { needed_bits } bits."
475+ )
476+
477+ if threshold > num_shares :
478+ raise ValueError (
479+ "Threshold number of shares must be less than or equal to the "
480+ "total shares specified."
481+ )
482+
483+ if not isinstance (pad_length , int ) or pad_length < 0 or pad_length > 1024 :
484+ raise ValueError (
485+ "Zero-pad length must be an integer between 0 and 1024 inclusive."
486+ )
487+
488+ secret = "1" + hex2bin (secret ) # prepend a 1 as a marker
489+
490+ secret = splitNumStringToIntArray (secret , pad_length )
491+
492+ num_shares = int (num_shares )
493+ threshold = int (threshold )
494+ # bits = 128 # Assuming bits as 128, you can adjust it accordingly
495+
496+ x = [None ] * num_shares
497+ y = [None ] * num_shares
498+
499+ for i in range (len (secret )):
500+ sub_shares = getShares (secret [i ], num_shares , threshold )
501+ for j in range (num_shares ):
502+ x [j ] = x [j ] or str (sub_shares [j ]["x" ])
503+ y [j ] = padLeft (bin (sub_shares [j ]["y" ])[2 :]) + (y [j ] or "" )
504+
505+ for i in range (num_shares ):
506+ x [i ] = constructPublicShareString (settings .bits , x [i ], bin2hex (y [i ]))
507+
508+ return x
509+
510+
360511# # Core Functions from secrets.js
361512# init = jsFunction('init')
362513# combine = jsFunction('combine')
0 commit comments