1
+ import secrets
2
+ import hashlib
3
+
4
+ # Arbitrary upper bound
5
+ MAX_SHARES = 32
6
+
7
+ # Order of secp256k1 elliptic curve
8
+ p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
9
+ R .< x > = PolynomialRing (FiniteField (p ))
10
+
11
+ def nonce32 (D , k , n , i ):
12
+ sha256 = hashlib .sha256 ()
13
+
14
+ sha256 .update (D .to_bytes (32 , "big" ))
15
+ sha256 .update (k .to_bytes (4 , "big" ))
16
+ sha256 .update (n .to_bytes (4 , "big" ))
17
+ sha256 .update (i .to_bytes (4 , "big" ))
18
+
19
+ sha256_hex = sha256 .hexdigest ()
20
+ nonce = int (sha256_hex , 16 ) % p
21
+
22
+ return ZZ (nonce )
23
+
24
+ def sss_get_shares (D , k , n ):
25
+ """
26
+ Splits the secret value D into n shares; k of n are needed
27
+ to reconstruct the secret.
28
+
29
+ D - 256-bit integer in [0, p)
30
+ """
31
+ assert 0 <= D < p
32
+ assert 1 < k <= n <= MAX_SHARES
33
+
34
+ # construct secret polynomial (degree = k-1)
35
+ q = D
36
+ for i in range (1 , k ):
37
+ a_i = nonce32 (int (D ), int (k ), int (n ), int (i ))
38
+ assert 0 < a_i < p
39
+ q += a_i * x ^ i
40
+
41
+ return [(i , q (i )) for i in range (1 , n + 1 )]
42
+
43
+ def _eval_lagrange_interpolate (t , xs , ys ):
44
+ """
45
+ Evaluates the iterpolated polynomial (uses lagrange basis
46
+ function) at point x.
47
+ t - 256-bit integer in [0, p)
48
+ xs - list - x co-ordinate of the shares
49
+ ys - list - y co-ordinate of the shares
50
+ """
51
+ # Algorithm
52
+ # 1. calc sum(y_i * delta_{j,x_i}) mod p, i = 0,1..k-1
53
+ # 2. delta_{j,x_i} = {(x -x0).(x -x1)...(x -x_k-1)/
54
+ # (xi-x0).(xi-x1)...(xi-x_k-1)} mod p
55
+ poly = R (0 )
56
+ for i in range (len (ys )):
57
+ delta = R (1 )
58
+ x_i = xs [i ]
59
+ y_i = ys [i ]
60
+ for x_j in xs :
61
+ if (x_j == x_i ):
62
+ continue
63
+ delta = delta * ((x - x_j )/ (x_i - x_j ))
64
+ poly += y_i * delta
65
+
66
+ return poly (t )
67
+
68
+
69
+ def sss_recover_secret (shares ):
70
+ """
71
+ Recovers the secret D from the given shares.
72
+
73
+ shares - list of points (x_i, y_i) on the polynomial
74
+ """
75
+ # Algorithm
76
+ # 1. assert for distinct x_i
77
+ # 2. evaluate the polynomial at 0
78
+ xs = [share [0 ] for share in shares ]
79
+ ys = [share [1 ] for share in shares ]
80
+ assert len (xs ) == len (set (xs ))
81
+
82
+ return _eval_lagrange_interpolate (0 , xs , ys )
83
+
84
+ def print_shares (shares ):
85
+ for i , fi in shares :
86
+ print ("({}, {})" .format (hex (int (i )).upper (), hex (int (fi )).upper ()))
87
+
88
+ def print_secret (secret ):
89
+ print ("secret: {}" .format (hex (int (secret )).upper ()))
90
+
91
+ def generate_test_vectors ():
92
+ seckey = 0xB7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF
93
+
94
+ print ("test_vector1:" )
95
+ shares1 = sss_get_shares (seckey , 2 , 3 )
96
+ print_shares (shares1 )
97
+ print_secret (sss_recover_secret (shares1 ))
98
+
99
+ print ("test_vector2:" )
100
+ shares2 = sss_get_shares (seckey , 3 , 5 )
101
+ print_shares (shares2 )
102
+ print_secret (sss_recover_secret (shares1 ))
103
+
104
+ print ("test_vector3:" )
105
+ shares3 = sss_get_shares (seckey , 4 , 7 )
106
+ print_shares (shares3 )
107
+ print_secret (sss_recover_secret (shares1 ))
108
+
109
+ if __name__ == '__main__' :
110
+ generate_test_vectors ()
0 commit comments