Skip to content

Commit f64c5a4

Browse files
committed
Added working combine(), conversion at 95%
1 parent 6ac68f6 commit f64c5a4

4 files changed

Lines changed: 246 additions & 0 deletions

File tree

gems/combine.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#! /usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
# vim: set et sw=4 fenc=utf-8:
4+
#
5+
# defaults.py
6+
7+
import js2pysecrets.node as node
8+
9+
import js2pysecrets as secrets
10+
from js2pysecrets.settings import Settings
11+
settings = Settings()
12+
13+
secrets.init()
14+
secret = secrets.random(32)
15+
shares = secrets.share(secret, 6, 3)
16+
17+
print("Secret: ", secret)
18+
print(secrets.combine(shares))
19+
20+
21+
22+
23+

gems/lagrange.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#! /usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
# vim: set et sw=4 fenc=utf-8:
4+
#
5+
# defaults.py
6+
7+
import js2pysecrets.node as node
8+
9+
import js2pysecrets as secrets
10+
from js2pysecrets.settings import Settings
11+
settings = Settings()
12+
13+
secrets.init()
14+
15+
print(secrets.lagrange(0, [2,3,5], [113,211,22]))
16+
17+
18+
print(secrets.lagrange(0, [2,3,5], (113,211,22)))
19+
20+
21+

js2pysecrets/base.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# from .wrapper import wrapper # Import your wrapper function
66

77
import math
8+
import re
89

910
from js2pysecrets.settings import Settings
1011

@@ -254,6 +255,33 @@ def horner(x, coeffs):
254255
return fx
255256

256257

258+
def lagrange(at, x, y):
259+
result = 0
260+
length = len(x)
261+
262+
for i in range(length):
263+
if y[i]:
264+
product = settings.logs[y[i]]
265+
266+
for j in range(length):
267+
if i != j:
268+
if at == x[j]:
269+
product = -1
270+
break
271+
product = (
272+
product
273+
+ settings.logs[at ^ x[j]]
274+
- settings.logs[x[i] ^ x[j]]
275+
+ settings.maxShares
276+
) % settings.maxShares
277+
278+
result = (
279+
result ^ settings.exps[product] if product != -1 else result
280+
)
281+
282+
return result
283+
284+
257285
def getShares(secret, num_shares, threshold):
258286
shares = []
259287
coeffs = [secret]
@@ -423,11 +451,104 @@ def hex2str(hex_string, bytes_per_char=None):
423451
return out
424452

425453

454+
def combine(shares, at=0):
455+
result = ""
456+
set_bits = None
457+
x = []
458+
y = []
459+
460+
at = at or 0
461+
462+
for share in shares:
463+
share_components = extractShareComponents(share)
464+
share_id = share_components["id"]
465+
share_data = share_components["data"]
466+
467+
# All shares must have the same bits settings.
468+
if set_bits is None:
469+
set_bits = share_components["bits"]
470+
elif set_bits != share_components["bits"]:
471+
raise ValueError("Mismatched shares: Different bit settings.")
472+
473+
# Reset everything to the bit settings of the shares.
474+
if settings.bits != set_bits:
475+
init(set_bits)
476+
477+
# Gathering all the provided shares
478+
if share_id not in x:
479+
x.append(share_id)
480+
split_share = splitNumStringToIntArray(hex2bin(share_data))
481+
y.append(split_share)
482+
483+
# Zipping all of the shares together.
484+
y = list(zip(*y))
485+
486+
# Extract the secret from the 'rotated' share data and return a
487+
# string of Binary digits which represent the secret directly. or in the
488+
# case of a newShare() return the binary string representing just that
489+
# new share.
490+
for yi in y:
491+
492+
result = padLeft(bin(lagrange(at, x, yi))[2:]) + result
493+
494+
return (
495+
bin2hex(result) if at >= 1 else bin2hex(result[result.find("1") + 1 :])
496+
)
497+
498+
426499
def getConfig():
427500
settings.update_defaults(hasCSPRNG=isSetRNG())
428501
return settings.get_config()
429502

430503

504+
def extractShareComponents(share):
505+
defaults = {
506+
"minBits": 1,
507+
"maxBits": 32,
508+
} # Assuming defaults for minBits and maxBits
509+
config = {"radix": 16} # Assuming radix as 16 for hex numbers
510+
511+
bits = int(share[0], 36)
512+
513+
if not (
514+
bits
515+
and isinstance(bits, int)
516+
and bits % 1 == 0
517+
and defaults["minBits"] <= bits <= defaults["maxBits"]
518+
):
519+
raise ValueError(
520+
f"Invalid share: Number of bits must be an integer between "
521+
f"{settings.min_bits} and {settings.max_bits}, inclusive."
522+
)
523+
524+
max_shares = 2**bits - 1
525+
id_len = len(hex(max_shares)[2:])
526+
527+
regex_str = (
528+
"^([a-kA-K3-9]{1})([a-fA-F0-9]{" + str(id_len) + "})([a-fA-F0-9]+)$"
529+
)
530+
share_components = re.match(regex_str, share)
531+
532+
if share_components:
533+
share_id = int(share_components.group(2), config["radix"])
534+
if not (
535+
isinstance(share_id, int)
536+
and share_id % 1 == 0
537+
and 1 <= share_id <= max_shares
538+
):
539+
raise ValueError(
540+
f"Invalid share: Share id must be an integer between 1 and "
541+
f"{max_shares}, inclusive."
542+
)
543+
return {
544+
"bits": bits,
545+
"id": share_id,
546+
"data": share_components.group(3),
547+
}
548+
549+
raise ValueError("Invalid share data provided.")
550+
551+
431552
def random(bits):
432553
if not isinstance(bits, int) or bits < 2 or bits > 65536:
433554
raise ValueError(

tests/test_basePrivate.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,3 +846,84 @@ def test_py_random_error():
846846
assert len(caught_warnings) == 1
847847
assert issubclass(caught_warnings[0].category, Warning)
848848
assert match in str(caught_warnings[0].message)
849+
850+
851+
def test_extractShareComponents_py(full_range_of_bits):
852+
# Generate shares with python
853+
secrets.init(full_range_of_bits)
854+
secret = secrets.random(128)
855+
shares = secrets.share(secret, 6, 3)
856+
shuffle = pieces(shares)
857+
858+
py_result = secrets.extractShareComponents(shuffle[0])
859+
js_result = node.extractShareComponents(shuffle[0])
860+
assert py_result["bits"] == js_result["bits"]
861+
assert py_result["id"] == js_result["id"]
862+
assert py_result["data"] == js_result["data"]
863+
864+
865+
def test_extractShareComponents_js(full_range_of_bits):
866+
secret = secrets.random(128)
867+
# Generate shares with node
868+
node_data = []
869+
node_data.append(node.init(full_range_of_bits, list=True))
870+
node_data.append(node.share(secret, 6, 3, list=True))
871+
js_results = chain(node_data)
872+
873+
shuffle = pieces(js_results[-1])
874+
875+
py_result = secrets.extractShareComponents(shuffle[0])
876+
js_result = node.extractShareComponents(shuffle[0])
877+
assert py_result["bits"] == js_result["bits"]
878+
assert py_result["id"] == js_result["id"]
879+
assert py_result["data"] == js_result["data"]
880+
881+
882+
def test_lagrange_simple():
883+
secrets.init()
884+
js_poly = node._lagrange(
885+
0, [1, 2, 3, 4, 5, 6], [79, 123, 68, 175, 144, 164]
886+
)
887+
polynomial = secrets.lagrange(
888+
0, [1, 2, 3, 4, 5, 6], [79, 123, 68, 175, 144, 164]
889+
)
890+
assert polynomial == js_poly
891+
polynomial = secrets.lagrange(0, [1, 2, 3, 4, 5], [79, 123, 68, 175, 144])
892+
assert polynomial == js_poly
893+
polynomial = secrets.lagrange(0, [1, 2, 3, 4], [79, 123, 68, 175])
894+
assert polynomial == js_poly
895+
polynomial = secrets.lagrange(0, [1, 2, 3], [79, 123, 68])
896+
assert polynomial == js_poly
897+
polynomial = secrets.lagrange(0, [1, 2], [79, 123])
898+
assert polynomial != js_poly
899+
polynomial = secrets.lagrange(0, [1], [79])
900+
assert polynomial != js_poly
901+
902+
903+
def test_combine_js2py(full_range_of_bits):
904+
secrets.init(full_range_of_bits)
905+
secret = secrets.random(128)
906+
# Generate shares with node
907+
node_data = []
908+
node_data.append(node.init(full_range_of_bits, list=True))
909+
node_data.append(node.share(secret, 6, 3, list=True))
910+
js_results = chain(node_data)
911+
912+
shuffle = pieces(js_results[-1])
913+
# print(shuffle)
914+
915+
py_result = secrets.combine(shuffle)
916+
assert py_result == secret
917+
918+
919+
def test_combine_py2py(full_range_of_bits):
920+
secrets.init(full_range_of_bits)
921+
secret = secrets.random(128)
922+
# Generate shares with node
923+
shares = secrets.share(secret, 6, 3)
924+
925+
shuffle = pieces(shares)
926+
# print(shuffle)
927+
928+
py_result = secrets.combine(shuffle)
929+
assert py_result == secret

0 commit comments

Comments
 (0)