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..3d0c690 --- /dev/null +++ b/src/lighthouseweb3/functions/encryption/generate.py @@ -0,0 +1,43 @@ +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 = [] + # 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": 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 new file mode 100644 index 0000000..947cecb --- /dev/null +++ b/tests/test_encryption/test_generate.py @@ -0,0 +1,19 @@ +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() + print(result) + 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