From 55f2d45228ac74899acfcc9bbb5c8c5995951e99 Mon Sep 17 00:00:00 2001 From: unniznd Date: Fri, 11 Jul 2025 09:10:20 +0530 Subject: [PATCH 1/2] feature: kavach generate key implemented and tested --- src/lighthouseweb3/__init__.py | 20 +++++++++++ .../functions/encryption/generate.py | 36 +++++++++++++++++++ tests/test_encryption/test_generate.py | 18 ++++++++++ 3 files changed, 74 insertions(+) create mode 100644 src/lighthouseweb3/functions/encryption/generate.py create mode 100644 tests/test_encryption/test_generate.py diff --git a/src/lighthouseweb3/__init__.py b/src/lighthouseweb3/__init__.py index b1d8d7c..8809da7 100644 --- a/src/lighthouseweb3/__init__.py +++ b/src/lighthouseweb3/__init__.py @@ -2,6 +2,10 @@ import os import io +from typing import Dict + +from .functions.encryption import generate as generateKey + from .functions import ( upload as d, deal_status, @@ -224,3 +228,19 @@ def getTagged(self, tag: str): except Exception as e: raise e +class Kavach: + @staticmethod + def generate(threshold: int = 3, key_count: int = 5) -> Dict[str, any]: + """ + Generates a set of master secret keys and corresponding key shards using BLS (Boneh-Lynn-Shacham) + threshold cryptography. + + :param threshold: int, The minimum number of key shards required to reconstruct the master key. + :param key_count: int, The total number of key shards to generate. + :return: Dict[str, any], A dictionary containing the master key and a list of key shards. + """ + + try: + return generateKey.generate(threshold, key_count) + except Exception as e: + raise e \ No newline at end of file diff --git a/src/lighthouseweb3/functions/encryption/generate.py b/src/lighthouseweb3/functions/encryption/generate.py new file mode 100644 index 0000000..536e8d9 --- /dev/null +++ b/src/lighthouseweb3/functions/encryption/generate.py @@ -0,0 +1,36 @@ +from py_ecc.bls import G2ProofOfPossession as BLS +from py_ecc.optimized_bls12_381 import curve_order +import random +from typing import Dict, List + +def int_to_bytes(x: int) -> bytes: + return x.to_bytes(32, byteorder="big") + +def eval_poly(poly: List[int], x: int) -> int: + """Evaluate polynomial at a given point x.""" + result = 0 + for i, coeff in enumerate(poly): + result = (result + coeff * pow(x, i, curve_order)) % curve_order + return result + +def generate(threshold: int = 3, key_count: int = 5) -> Dict[str, any]: + if threshold > key_count: + raise ValueError("threshold must be less than or equal to key_count") + + # Generate random polynomial coefficients (secret is constant term) + poly = [random.randint(1, curve_order - 1) for _ in range(threshold)] + master_sk = poly[0] # constant term is master key + + shares = [] + for i in range(1, key_count + 1): + x = i + y = eval_poly(poly, x) + shares.append({ + "index": x, + "key": int_to_bytes(y).hex() + }) + + return { + "masterKey": int_to_bytes(master_sk).hex(), + "keyShards": shares + } diff --git a/tests/test_encryption/test_generate.py b/tests/test_encryption/test_generate.py new file mode 100644 index 0000000..4f68e57 --- /dev/null +++ b/tests/test_encryption/test_generate.py @@ -0,0 +1,18 @@ +import unittest +from src.lighthouseweb3 import Kavach +from unittest.mock import patch + +class TestGenerateKey(unittest.TestCase): + def test_generate_key_success(self): + result = Kavach.generate() + self.assertIsInstance(result["masterKey"], str, "masterKey should be a string") + self.assertEqual(len(result["keyShards"]), 5, "keyShards should have length 5") + + def test_threshold_greater_than_key_count(self): + with self.assertRaises(ValueError) as context: + Kavach.generate(threshold=6, key_count=5) + self.assertEqual( + str(context.exception), + "threshold must be less than or equal to key_count", + "Expected ValueError for threshold > key_count" + ) \ No newline at end of file From 82346e834e6091abecc3becd920c09fdb58e9b88 Mon Sep 17 00:00:00 2001 From: unniznd Date: Mon, 21 Jul 2025 21:33:54 +0530 Subject: [PATCH 2/2] fixed interops with javascript --- .../functions/encryption/generate.py | 15 +++++++++++---- tests/test_encryption/test_generate.py | 1 + 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/lighthouseweb3/functions/encryption/generate.py b/src/lighthouseweb3/functions/encryption/generate.py index 536e8d9..3d0c690 100644 --- a/src/lighthouseweb3/functions/encryption/generate.py +++ b/src/lighthouseweb3/functions/encryption/generate.py @@ -22,15 +22,22 @@ def generate(threshold: int = 3, key_count: int = 5) -> Dict[str, any]: master_sk = poly[0] # constant term is master key shares = [] - for i in range(1, key_count + 1): - x = i + # Generate random vector IDs, ensuring uniqueness + id_vec = set() + while len(id_vec) < key_count: + id_vec.add(random.randint(1, curve_order - 1)) + id_vec = list(id_vec) + + for x in id_vec: y = eval_poly(poly, x) + # Convert index to hex string without '0x' prefix to match JS output + index_hex = hex(x)[2:].zfill(64) # Ensure 64-character hex string shares.append({ - "index": x, + "index": index_hex, "key": int_to_bytes(y).hex() }) return { "masterKey": int_to_bytes(master_sk).hex(), "keyShards": shares - } + } \ No newline at end of file diff --git a/tests/test_encryption/test_generate.py b/tests/test_encryption/test_generate.py index 4f68e57..947cecb 100644 --- a/tests/test_encryption/test_generate.py +++ b/tests/test_encryption/test_generate.py @@ -5,6 +5,7 @@ class TestGenerateKey(unittest.TestCase): def test_generate_key_success(self): result = Kavach.generate() + print(result) self.assertIsInstance(result["masterKey"], str, "masterKey should be a string") self.assertEqual(len(result["keyShards"]), 5, "keyShards should have length 5")