Skip to content

Commit 4d4f7a2

Browse files
Merge pull request #73 from jo7ueb/issue72
Fix vulnerability of to_key_shares() in bsv/keys.py
2 parents a375094 + d711270 commit 4d4f7a2

File tree

1 file changed

+33
-6
lines changed

1 file changed

+33
-6
lines changed

bsv/keys.py

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import hashlib
22
import hmac
3+
import os
34
from base64 import b64encode, b64decode
45
from typing import Optional, Union, Callable, Tuple
56

@@ -11,7 +12,7 @@
1112
from .constants import Network, NETWORK_ADDRESS_PREFIX_DICT, NETWORK_WIF_PREFIX_DICT, PUBLIC_KEY_COMPRESSED_PREFIX_LIST
1213
from .curve import Point
1314
from .curve import curve, curve_multiply as curve_multiply, curve_add as curve_add
14-
from .hash import hash160, hash256, hmac_sha256
15+
from .hash import hash160, hash256, hmac_sha256, hmac_sha512
1516
from .utils import decode_wif, text_digest, stringify_ecdsa_recoverable, unstringify_ecdsa_recoverable
1617
from .utils import deserialize_ecdsa_recoverable, serialize_ecdsa_der
1718
from .polynomial import Polynomial, PointInFiniteField, KeyShares
@@ -403,13 +404,39 @@ def to_key_shares(self, threshold: int, total_shares: int) -> 'KeyShares':
403404

404405
# Generate shares
405406
points = []
407+
used_x_coordinates = set()
408+
409+
# Cryptographically secure x-coordinate generation for Shamir's Secret Sharing (toKeyShares)
410+
#
411+
# - Each x-coordinate is derived using a master seed (Random(64)) as the HMAC key and a per-attempt counter array as the message.
412+
# - The counter array includes the share index, the attempt number (to handle rare collisions), and 32 bytes of fresh randomness for each attempt.
413+
# - This ensures:
414+
# 1. **Non-determinism**: Each split is unique, even for the same key and parameters, due to the per-attempt randomness.
415+
# 2. **Uniqueness**: x-coordinates are checked for zero and duplication; retry logic ensures no repeats or invalid values.
416+
# 3. **Cryptographic strength**: HMAC-SHA-512 is robust, and combining deterministic and random values protects against RNG compromise or bias.
417+
# 4. **Defensive programming**: Attempts are capped (5 per share) to prevent infinite loops in pathological cases.
418+
#
419+
# This approach is robust against all practical attacks and is suitable for high-security environments where deterministic splits are not desired.
420+
421+
seed = os.urandom(64)
422+
406423
for i in range(total_shares):
407-
# Generate random x coordinate using a new private key
408-
# Using private_key.key.to_int() based on the structure in keys.py
409-
random_private_key = PrivateKey()
410-
x = random_private_key.int()
424+
x = None
425+
attempts = 0
426+
427+
# TypeScript版と同様の安全なx座標生成
428+
while x is None or x == 0 or x in used_x_coordinates:
429+
counter = [i, attempts] + list(os.urandom(32))
430+
counter_bytes = bytes(counter)
431+
432+
h = hmac_sha512(seed, counter_bytes)
433+
x = int.from_bytes(h, 'big') % curve.p
434+
435+
attempts += 1
436+
if attempts > 5:
437+
raise ValueError('Failed to generate unique x coordinate after 5 attempts')
411438

412-
# Evaluate polynomial at x to get y coordinate
439+
used_x_coordinates.add(x)
413440
y = poly.value_at(x)
414441

415442
# Create a point and add to points' list

0 commit comments

Comments
 (0)