|
1 | 1 | import hashlib |
2 | 2 | import hmac |
| 3 | +import os |
3 | 4 | from base64 import b64encode, b64decode |
4 | 5 | from typing import Optional, Union, Callable, Tuple |
5 | 6 |
|
|
11 | 12 | from .constants import Network, NETWORK_ADDRESS_PREFIX_DICT, NETWORK_WIF_PREFIX_DICT, PUBLIC_KEY_COMPRESSED_PREFIX_LIST |
12 | 13 | from .curve import Point |
13 | 14 | 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 |
15 | 16 | from .utils import decode_wif, text_digest, stringify_ecdsa_recoverable, unstringify_ecdsa_recoverable |
16 | 17 | from .utils import deserialize_ecdsa_recoverable, serialize_ecdsa_der |
17 | 18 | from .polynomial import Polynomial, PointInFiniteField, KeyShares |
@@ -403,13 +404,39 @@ def to_key_shares(self, threshold: int, total_shares: int) -> 'KeyShares': |
403 | 404 |
|
404 | 405 | # Generate shares |
405 | 406 | 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 | + |
406 | 423 | 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') |
411 | 438 |
|
412 | | - # Evaluate polynomial at x to get y coordinate |
| 439 | + used_x_coordinates.add(x) |
413 | 440 | y = poly.value_at(x) |
414 | 441 |
|
415 | 442 | # Create a point and add to points' list |
|
0 commit comments