Shamir秘密共享方案 (Python)


Shamir’s Secret Sharing scheme is an important cryptographic algorithm that allows private information— “secrets” — to be distributed securely amongst an untrusted network.

Shamir’s method for secret sharing relies on polynomial interpolation, which is an algebraic method of estimating unknown values in a gap between two known data points — without needing to know anything about what is on either side of those points.

SSS encodes a “secret” into a polynomial, then splits it into pieces and distributes it It’s possible to use polynomial interpolation to efficiently reconstruct that secret without requiring every single share. Instead only the threshold is needed, which provides enough points of data to correctly estimate the values between gaps in the encrypted shares.

REF: https://medium.com/keylesstech/a-beginners-guide-to-shamir-s-secret-sharing-e864efbf3648

Shamir秘密共享方案,叫做Shamir Secret Sharing, SSS。是由Shamir提出的一個分享密鑰(本文秘密和“密鑰”同義)的局部、並共同計算密鑰的方法。

設計目的

假設公司A,B,C有一個密鑰可以打開共同的倉庫房門,為了防止保管人不在,或者被侵害,或者鑰匙被偷,或者監守自盜。需要設計一個分享秘密的方案。

一個直觀的方案就是將秘密分開為3份,給ABC各自鑰匙的一部分,他們的子密鑰合起來才能打開房門。

數學定義

引入一個臨界點(Threshold,也叫門檻)的概念。N個分享秘密的人,只要湊夠k個人(k<=N)就可以重建秘密。k就是這個臨界點。

k<N,大大增加了使用的便利性和可擴展性。任意的k個人合起來都可以重建秘密,少於k個人無法重建秘密。

k=N,表明方案中所有的參與者需要貢獻出自己的子秘密,才能合成所需的秘密。

舉例

一個(k,N) 臨界點方案,其共享秘密是 S.

對一個k-1次的多項式,取 N個不一樣的點(i,f(i))。那么只要湊夠 k個點就可以接出系數(a0,a1,……ak-1)。

只要把N個點分給N個人,設某個系數為共同秘密(如a0是秘密),那么就等於實現了SSS算法。

秘密分配及還原過程

首先介紹一個小費馬定理:
在這里插入圖片描述
引申為:
在這里插入圖片描述
那么有:
在這里插入圖片描述

秘密碎片生成:

構造一個多項式

在這里插入圖片描述

其中,S為我們的秘密,p為素數,且S < p

取w個不相等的x,帶入F(x)中,得到w組(xi,yi),分配給w個人

公開p,銷毀多項式,每個人負責保密自己的(xi,yi)

秘密恢復:

當x=0時,F(0)=S,即可恢復出S

將t組(xi,yi)帶入下式即可

在這里插入圖片描述

其中,負一次方為該項模p的逆

將t組(xi,yi)帶入即可得到S

示例:

假設我們有w=4個人,設定至少t=3人才能恢復秘密。

秘密S=2,p=23

構造在這里插入圖片描述

取x1=1,x2=2,x3=3,x4=4

帶入得y1=7,y2=16, y3=6,y4=0

利用3組進行恢復(1,7) (3,6) (4,0)

在這里插入圖片描述

計算可得到S=2

一個簡單的恢復腳本

 1 # coding:utf-8
 2 
 3 
 4 def oj(a, n):   # 求逆的函數
 5     a = a % n
 6     s = [0, 1]
 7     while a != 1:
 8         if a == 0:
 9             return 0
10         q = n/a
11         t = n % a
12         n = a
13         a = t
14         s += [s[-2] - q * s[-1]]
15     return s[-1]
16 
17 p = 23
18 m = ((4, 0),
19      (3, 6),
20      (2, 16))
21 r = (
22     m[0][1] * (0 - m[1][0]) * (0 - m[2][0]) * oj((m[0][0] - m[1][0]) * (m[0][0] - m[2][0]), p) +
23     m[1][1] * (0 - m[0][0]) * (0 - m[2][0]) * oj((m[1][0] - m[0][0]) * (m[1][0] - m[2][0]), p) +
24     m[2][1] * (0 - m[0][0]) * (0 - m[1][0]) * oj((m[2][0] - m[0][0]) * (m[2][0] - m[1][0]), p)
25      ) % p
26 print r

另外一個腳本

 1 import Crypto.Util.number as numb
 2 import random
 3 
 4 
 5 # 求逆的函數,之前的版本用python2寫的,這次用的python3,只把整除符號改了一下
 6 def oj(a, n):
 7     a = a % n
 8     s = [0, 1]
 9     while a != 1:
10         if a == 0:
11             return 0
12         q = n // a
13         t = n % a
14         n = a
15         a = t
16         s += [s[-2] - q * s[-1]]
17     return s[-1]
18 
19 
20 # max_length 為p的長度,同時也是秘密的最大長度
21 # secret_is_text =0 默認輸入時文本, 非0時認為是數字
22 # p 默認為0, 會根據max_length 自動生成,不為0時直接使用,需要保證p為素數, 函數內沒有素性檢驗
23 def create(max_length=513, secret_is_text=0, p=0):
24     if not p:
25         p = numb.getPrime(max_length)
26 
27     w = int(input("請輸入秘密保存人數:"))
28     t = int(input("請輸入秘密恢復所需人數:"))
29     while not (t > 0 and t <= w):
30         t = int(input("請重新輸入:"))
31     s = input("請輸入你的秘密:")
32 
33     if secret_is_text:
34         s = numb.bytes_to_long(s.encode("utf-8"))
35     else:
36         try:
37             s = int(s)
38         except Exception as e:
39             s = numb.bytes_to_long(s.encode("utf-8"))
40 
41     x_list = list()
42     a_list = list()
43     i = w
44     while i > 0:
45         x = random.randint(p // 2, p)  # 該范圍沒有特定限制,如果想讓xi,yi取小一點兒的話可把范圍寫小點兒,但是要大於w
46         if x not in x_list:
47             x_list.append(x)
48             i -= 1
49 
50     for a in range(t):
51         a_list.append(random.randint(p // 2, p))  # 同上
52 
53     result = list()
54     for x in x_list:
55         y = s
56         for a_n in range(t):
57             a = a_list[i]
58             y += a * pow(x, i + 1, p)
59         result.append((x, y))
60     return t, p, result
61 
62 
63 # get_text=1 默認恢復為字符串,若想得到數字填0
64 def restore(p, information, get_text=1):
65 
66     x_list = list()
67     y_list=list()
68     for x, y in information:
69         x_list.append(x)
70         y_list.append(y)
71 
72     s = 0
73     for x_i in range(len(x_list)):
74         tmp_num = y_list[x_i]
75         x_i_j = 1
76         for x_j in range(len(x_list)):
77             if x_i != x_j:
78                 tmp_num = tmp_num * (0 - x_list[x_j]) % p
79                 x_i_j *= x_list[x_i] - x_list[x_j]
80         tmp_num = tmp_num * oj(x_i_j, p) % p
81         s += tmp_num
82 
83     s = s % p
84     print(s)
85     if get_text:
86         try:
87             s = numb.long_to_bytes(s)
88             s = s.decode("utf-8")
89         except Exception as e:
90             print(e)
91 
92     return s
93 
94 
95 t, p, result = create()             #result為秘密碎片的列表
96 print(result)
97 print(restore(p, result[:t]))     #這里我取了result的前t個,實際中可以取任意t個。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM