threshold_sharing¶
Threshold Secret Sharing for DKLS23 and Threshold ECDSA
type: secret sharing
setting: Elliptic Curve group
assumption: DLP (for Feldman VSS)
This module extends Shamir secret sharing for threshold ECDSA requirements, providing Feldman VSS, Pedersen commitments, and EC group element support.
- class threshold_sharing.PedersenVSS(groupObj: Any)[source]¶
Bases:
ThresholdSharingPedersen VSS with information-theoretic hiding
Uses two generators g, h for commitments where the discrete log relationship between g and h is unknown. This provides unconditional hiding of the secret, unlike Feldman VSS.
>>> from charm.toolbox.eccurve import secp256k1 >>> group = ECGroup(secp256k1) >>> pvss = PedersenVSS(group) >>> g = group.random(G) >>> h = group.random(G) >>> secret = group.random(ZR) >>> shares, blindings, comms = pvss.share_with_blinding(secret, g, h, 2, 3) >>> pvss.verify_pedersen_share(1, shares[1], blindings[1], comms, g, h) True >>> pvss.verify_pedersen_share(2, shares[2], blindings[2], comms, g, h) True >>> pvss.verify_pedersen_share(3, shares[3], blindings[3], comms, g, h) True >>> recovered = pvss.reconstruct({1: shares[1], 2: shares[2]}, 2) >>> secret == recovered True
- combine_pedersen_commitments(commitments_list: List[List[Any]]) List[Any][source]¶
Combine multiple Pedersen commitments (for DKG)
When multiple dealers contribute shares, their commitments can be combined element-wise.
- Args:
commitments_list: List of commitment lists from different dealers
- Returns:
Combined commitments list
>>> from charm.toolbox.eccurve import secp256k1 >>> group = ECGroup(secp256k1) >>> pvss = PedersenVSS(group) >>> g, h = group.random(G), group.random(G) >>> s1, s2 = group.random(ZR), group.random(ZR) >>> _, _, comms1 = pvss.share_with_blinding(s1, g, h, 2, 3) >>> _, _, comms2 = pvss.share_with_blinding(s2, g, h, 2, 3) >>> combined = pvss.combine_pedersen_commitments([comms1, comms2]) >>> len(combined) == len(comms1) True
Share with Pedersen commitments (information-theoretically hiding)
Creates two polynomials: - f(x) with f(0) = secret for the actual shares - r(x) with r(0) = random blinding for hiding
Commitments are C_j = g^{a_j} * h^{b_j} where a_j, b_j are coefficients of f and r respectively.
- Args:
secret: The secret to share (ZR element) g: First generator point h: Second generator point (discrete log to g unknown) threshold: Minimum shares needed to reconstruct num_parties: Total number of parties
- Returns:
Tuple of (shares_dict, blindings_dict, commitments_list) - shares_dict: {party_id: share_value} - blindings_dict: {party_id: blinding_value} - commitments_list: [C_0, C_1, …, C_{t-1}]
>>> from charm.toolbox.eccurve import secp256k1 >>> group = ECGroup(secp256k1) >>> pvss = PedersenVSS(group) >>> g, h = group.random(G), group.random(G) >>> secret = group.random(ZR) >>> shares, blindings, comms = pvss.share_with_blinding(secret, g, h, 2, 4) >>> all(pvss.verify_pedersen_share(i, shares[i], blindings[i], comms, g, h) ... for i in range(1, 5)) True
Verify a share against Pedersen commitments
Checks that g^{share} * h^{blinding} == prod_{j=0}^{t-1} C_j^{i^j}
- Args:
party_id: The party’s identifier (1 to n) share: The share value (ZR element) blinding: The blinding value (ZR element) commitments: List of Pedersen commitments [C_0, …, C_{t-1}] g: First generator point h: Second generator point
- Returns:
True if share is valid, False otherwise
>>> from charm.toolbox.eccurve import secp256k1 >>> group = ECGroup(secp256k1) >>> pvss = PedersenVSS(group) >>> g, h = group.random(G), group.random(G) >>> secret = group.random(ZR) >>> shares, blindings, comms = pvss.share_with_blinding(secret, g, h, 3, 5) >>> pvss.verify_pedersen_share(3, shares[3], blindings[3], comms, g, h) True
- class threshold_sharing.ThresholdSharing(groupObj: Any)[source]¶
Bases:
objectEnhanced secret sharing for threshold ECDSA
Supports Feldman VSS and operations on EC groups.
Curve Agnostic¶
This implementation supports any elliptic curve group that is DDH-hard. The curve is specified via the groupObj parameter.
>>> from charm.toolbox.eccurve import secp256k1 >>> group = ECGroup(secp256k1) >>> ts = ThresholdSharing(group) >>> g = group.random(G) >>> secret = group.random(ZR) >>> shares, commitments = ts.share_with_verification(secret, g, threshold=2, num_parties=3) >>> ts.verify_share(1, shares[1], commitments, g) True >>> ts.verify_share(2, shares[2], commitments, g) True >>> ts.verify_share(3, shares[3], commitments, g) True >>> recovered = ts.reconstruct({1: shares[1], 2: shares[2]}, threshold=2) >>> secret == recovered True
Add two sets of shares (for additive share combination)
Useful for distributed key generation and refreshing.
- Args:
shares1: First dictionary of shares {party_id: share} shares2: Second dictionary of shares {party_id: share}
- Returns:
Dictionary of combined shares
>>> from charm.toolbox.eccurve import secp256k1 >>> group = ECGroup(secp256k1) >>> ts = ThresholdSharing(group) >>> s1, s2 = group.random(ZR), group.random(ZR) >>> shares1 = ts.share(s1, 2, 3) >>> shares2 = ts.share(s2, 2, 3) >>> combined = ts.add_shares(shares1, shares2) >>> recovered = ts.reconstruct({1: combined[1], 2: combined[2]}, 2) >>> recovered == s1 + s2 True
- lagrange_coefficient(party_ids: List[int], i: int, x: int = 0) Any[source]¶
Compute Lagrange coefficient for party i at point x
L_i(x) = prod_{j != i} (x - j) / (i - j)
- Args:
party_ids: List of party identifiers in the reconstruction set i: The party for which to compute the coefficient x: The evaluation point (default 0 for secret recovery)
- Returns:
The Lagrange coefficient as a ZR element
>>> from charm.toolbox.eccurve import secp256k1 >>> group = ECGroup(secp256k1) >>> ts = ThresholdSharing(group) >>> coeff = ts.lagrange_coefficient([1, 2, 3], 1, x=0) >>> # L_1(0) = (0-2)(0-3) / (1-2)(1-3) = 6/2 = 3
- reconstruct(shares: Dict[int, Any], threshold: int) Any[source]¶
Reconstruct secret from threshold shares using Lagrange interpolation
- Args:
shares: Dictionary {party_id: share_value} with at least threshold entries threshold: The threshold used when sharing
- Returns:
The reconstructed secret
- Raises:
ValueError: If fewer than threshold shares provided
>>> from charm.toolbox.eccurve import secp256k1 >>> group = ECGroup(secp256k1) >>> ts = ThresholdSharing(group) >>> secret = group.random(ZR) >>> shares = ts.share(secret, threshold=3, num_parties=5) >>> recovered = ts.reconstruct({1: shares[1], 2: shares[2], 4: shares[4]}, 3) >>> secret == recovered True
Refresh shares for proactive security
Generates new shares of zero and adds them to existing shares. The new shares reconstruct to the same secret but are unlinkable to the old shares.
- Args:
shares: Dictionary of current shares {party_id: share} threshold: The threshold of the sharing
- Returns:
Dictionary of refreshed shares
>>> from charm.toolbox.eccurve import secp256k1 >>> group = ECGroup(secp256k1) >>> ts = ThresholdSharing(group) >>> secret = group.random(ZR) >>> shares = ts.share(secret, 2, 3) >>> refreshed = ts.refresh_shares(shares, 2) >>> recovered = ts.reconstruct({1: refreshed[1], 3: refreshed[3]}, 2) >>> secret == recovered True
Basic Shamir secret sharing
- Args:
secret: The secret to share (ZR element) threshold: Minimum number of shares needed to reconstruct (t) num_parties: Total number of parties (n)
- Returns:
Dictionary mapping party_id (1 to n) to share values
>>> from charm.toolbox.eccurve import secp256k1 >>> group = ECGroup(secp256k1) >>> ts = ThresholdSharing(group) >>> secret = group.random(ZR) >>> shares = ts.share(secret, threshold=2, num_parties=4) >>> len(shares) == 4 True >>> recovered = ts.reconstruct({1: shares[1], 3: shares[3]}, threshold=2) >>> secret == recovered True
Feldman VSS - shares with public commitments for verification
Creates shares using Shamir’s scheme and publishes commitments C_j = g^{a_j} for each coefficient a_j, allowing verification without revealing the secret.
- Args:
secret: The secret to share (ZR element) generator: Generator point g in the EC group (G element) threshold: Minimum shares needed to reconstruct num_parties: Total number of parties
- Returns:
Tuple of (shares_dict, commitments_list) - shares_dict: {party_id: share_value} - commitments_list: [C_0, C_1, …, C_{t-1}] where C_j = g^{a_j}
>>> from charm.toolbox.eccurve import secp256k1 >>> group = ECGroup(secp256k1) >>> ts = ThresholdSharing(group) >>> g = group.random(G) >>> secret = group.random(ZR) >>> shares, comms = ts.share_with_verification(secret, g, 2, 3) >>> all(ts.verify_share(i, shares[i], comms, g) for i in range(1, 4)) True
Verify a share against Feldman commitments
Checks that g^{share} == prod_{j=0}^{t-1} C_j^{i^j}
- Args:
party_id: The party’s identifier (1 to n) share: The share value to verify (ZR element) commitments: List of Feldman commitments [C_0, …, C_{t-1}] generator: Generator point g used in commitments
- Returns:
True if share is valid, False otherwise