From 4ab48d2323630b702b6a07adb0966d1ef836eb4d Mon Sep 17 00:00:00 2001 From: Mostafa Date: Wed, 16 Jul 2025 18:45:37 +0800 Subject: [PATCH 1/2] feat: split secret for BLS --- README.md | 2 +- examples/example_bls_threshold.py | 31 +++++++++++++++++++++++++++++++ pactus/crypto/bls/private_key.py | 24 ++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 examples/example_bls_threshold.py diff --git a/README.md b/README.md index 075687a..dfc16a2 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Explore the `examples` folder for more detailed usage scenarios. For local development, you can install the package in editable mode, which allows you to make changes and test them immediately: ```bash -python3 -m pip install . +python3 -m pip install -e . ``` After making changes, it's important to ensure all tests pass by running: diff --git a/examples/example_bls_threshold.py b/examples/example_bls_threshold.py new file mode 100644 index 0000000..bd7bfed --- /dev/null +++ b/examples/example_bls_threshold.py @@ -0,0 +1,31 @@ +from pactus.crypto.bls.private_key import PrivateKey +from pactus.crypto.bls.signature import Signature +from itertools import combinations + + + +# Example of BLS threshold signature aggregation using Shamir's Secret Sharing +# This example demonstrates how to create a threshold signature scheme +# where a subset of signers can create a valid signature. + + +def main() -> None: + msg = "some message".encode() + N = 3 # Number of signers + T = 2 # Threshold for aggregation + + msk = PrivateKey.random() # Master Secret Key + mpk = msk.public_key() + + print(f"Master Secret Key: {msk.raw_bytes().hex()}") + print(f"Master Public Key: {mpk.string()}") + + shares = msk.split(N, T) + + for i, sk in enumerate(shares): + print(f"Private Key Share {i + 1}: {sk.raw_bytes().hex()}") + + + +if __name__ == "__main__": + main() diff --git a/pactus/crypto/bls/private_key.py b/pactus/crypto/bls/private_key.py index 48830a6..fc4ac25 100644 --- a/pactus/crypto/bls/private_key.py +++ b/pactus/crypto/bls/private_key.py @@ -79,3 +79,27 @@ def public_key(self) -> PublicKey: def sign(self, msg: bytes) -> Signature: point_g1 = sign(self.scalar, msg, ciphersuite=DST) return Signature(point_g1) + + def split(self, n: int, t: int) -> list[PrivateKey]: + if n < t: + raise ValueError("n must be greater than t") + + if n < 1: + raise ValueError("n must be greater than 0") + + # Create the coefficients for the polynomial: c[0] = secret, c[1..t-1] = random private keys + coeffs = [self.scalar] + for _ in range(1, t): + rand_priv = PrivateKey.random() + coeffs.append(rand_priv.scalar) + + # Generate n shares by evaluating the polynomial at x = 1..n + shares = [] + for i in range(1, n + 1): + share_scalar = utils.evaluate_polynomial(coeffs, i, curve_order) + if share_scalar is None: + raise ValueError(f"Failed to evaluate polynomial at x={i}") + shares.append(PrivateKey(share_scalar)) + + return shares + From 59934c1ee886771067778f94ad8d22b641987d96 Mon Sep 17 00:00:00 2001 From: Mostafa Date: Wed, 16 Jul 2025 18:54:59 +0800 Subject: [PATCH 2/2] chore: fix linting issues --- examples/example_bls_threshold.py | 8 ++------ pactus/crypto/bls/private_key.py | 11 +++++++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/examples/example_bls_threshold.py b/examples/example_bls_threshold.py index bd7bfed..49ef299 100644 --- a/examples/example_bls_threshold.py +++ b/examples/example_bls_threshold.py @@ -1,7 +1,4 @@ from pactus.crypto.bls.private_key import PrivateKey -from pactus.crypto.bls.signature import Signature -from itertools import combinations - # Example of BLS threshold signature aggregation using Shamir's Secret Sharing @@ -10,11 +7,11 @@ def main() -> None: - msg = "some message".encode() + # msg = "some message".encode() N = 3 # Number of signers T = 2 # Threshold for aggregation - msk = PrivateKey.random() # Master Secret Key + msk = PrivateKey.random() # Master Secret Key mpk = msk.public_key() print(f"Master Secret Key: {msk.raw_bytes().hex()}") @@ -26,6 +23,5 @@ def main() -> None: print(f"Private Key Share {i + 1}: {sk.raw_bytes().hex()}") - if __name__ == "__main__": main() diff --git a/pactus/crypto/bls/private_key.py b/pactus/crypto/bls/private_key.py index fc4ac25..3027e0c 100644 --- a/pactus/crypto/bls/private_key.py +++ b/pactus/crypto/bls/private_key.py @@ -82,10 +82,12 @@ def sign(self, msg: bytes) -> Signature: def split(self, n: int, t: int) -> list[PrivateKey]: if n < t: - raise ValueError("n must be greater than t") + msg = f"n must be greater than t: n={n}, t={t}" + raise ValueError(msg) if n < 1: - raise ValueError("n must be greater than 0") + msg = f"n must be greater than 1: n={n}" + raise ValueError(msg) # Create the coefficients for the polynomial: c[0] = secret, c[1..t-1] = random private keys coeffs = [self.scalar] @@ -98,8 +100,9 @@ def split(self, n: int, t: int) -> list[PrivateKey]: for i in range(1, n + 1): share_scalar = utils.evaluate_polynomial(coeffs, i, curve_order) if share_scalar is None: - raise ValueError(f"Failed to evaluate polynomial at x={i}") + msg = f"Failed to evaluate polynomial at x={i}" + raise ValueError(msg) + shares.append(PrivateKey(share_scalar)) return shares -