Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 33 additions & 6 deletions bsv/keys.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import hashlib
import hmac
import os
from base64 import b64encode, b64decode
from typing import Optional, Union, Callable, Tuple

Expand All @@ -11,7 +12,7 @@
from .constants import Network, NETWORK_ADDRESS_PREFIX_DICT, NETWORK_WIF_PREFIX_DICT, PUBLIC_KEY_COMPRESSED_PREFIX_LIST
from .curve import Point
from .curve import curve, curve_multiply as curve_multiply, curve_add as curve_add
from .hash import hash160, hash256, hmac_sha256
from .hash import hash160, hash256, hmac_sha256, hmac_sha512
from .utils import decode_wif, text_digest, stringify_ecdsa_recoverable, unstringify_ecdsa_recoverable
from .utils import deserialize_ecdsa_recoverable, serialize_ecdsa_der
from .polynomial import Polynomial, PointInFiniteField, KeyShares
Expand Down Expand Up @@ -403,13 +404,39 @@ def to_key_shares(self, threshold: int, total_shares: int) -> 'KeyShares':

# Generate shares
points = []
used_x_coordinates = set()

# Cryptographically secure x-coordinate generation for Shamir's Secret Sharing (toKeyShares)
#
# - Each x-coordinate is derived using a master seed (Random(64)) as the HMAC key and a per-attempt counter array as the message.
# - The counter array includes the share index, the attempt number (to handle rare collisions), and 32 bytes of fresh randomness for each attempt.
# - This ensures:
# 1. **Non-determinism**: Each split is unique, even for the same key and parameters, due to the per-attempt randomness.
# 2. **Uniqueness**: x-coordinates are checked for zero and duplication; retry logic ensures no repeats or invalid values.
# 3. **Cryptographic strength**: HMAC-SHA-512 is robust, and combining deterministic and random values protects against RNG compromise or bias.
# 4. **Defensive programming**: Attempts are capped (5 per share) to prevent infinite loops in pathological cases.
#
# This approach is robust against all practical attacks and is suitable for high-security environments where deterministic splits are not desired.

seed = os.urandom(64)

for i in range(total_shares):
# Generate random x coordinate using a new private key
# Using private_key.key.to_int() based on the structure in keys.py
random_private_key = PrivateKey()
x = random_private_key.int()
x = None
attempts = 0

# TypeScript版と同様の安全なx座標生成
while x is None or x == 0 or x in used_x_coordinates:
counter = [i, attempts] + list(os.urandom(32))
counter_bytes = bytes(counter)

h = hmac_sha512(seed, counter_bytes)
x = int.from_bytes(h, 'big') % curve.p

attempts += 1
if attempts > 5:
raise ValueError('Failed to generate unique x coordinate after 5 attempts')

# Evaluate polynomial at x to get y coordinate
used_x_coordinates.add(x)
y = poly.value_at(x)

# Create a point and add to points' list
Expand Down