盲签名
Chaum盲签名协议
协议流程:
\[\begin{flalign}
&Setup:\\
&\quad p,q = getPrime(safe.bit\_length);n = p * q;Pubkey = (n, e);Pravitekey = d\\
&Sign:\\
&\quad 盲化:USER\ choose\ k \in R[1,n - 1],compute\ m'\equiv m\cdot k^e(mod\ n),m'\rightarrow Signer\\
&\quad 签名:Signer\ compute\ s'\equiv (m')^d(mod\ n),s'\rightarrow USER\\
&\quad 去盲;USER\ compute\ s \equiv k^{-1}\cdot s'(mod\ n),(m,s)\rightarrow Verifier\\
&Verify:\\
&\quad return\quad s^e(mod \ n) == m&
\end{flalign}
\]
The attack I attempted on an impulse
- 阅读教材时发现,在选举系统中,投票结束后会公开(m,s),本人最初认为如果公开(m,s)就不再满足防追踪性(不可关联性)。具体攻击(后续也证明这种思路是不可行的)思路如下:
\[\begin{flalign}
&Target:选定一组(m',s'),若寻找到该(m',s')所对应的(m,s)则攻击成功.\\
&\quad 设公开的所有选民的(m,s)为(m,s)group.由m_i'\equiv m_i \cdot k_i^e(mod\ n);s_i'\equiv m_i^d\cdot k_i(mod\ n);s_i\equiv m_i^d(mod\ n)\rightarrow k_i\equiv s_i'\cdot s_i^{-1}(mod\ n)\\
&Attack:\\
&设选定(m_j',s_j')\\
&\quad for\ i \ in\ 1..num\\
&\quad\quad k' \equiv s_j' \cdot s_i^{-1}(mod\ n)\\
&\quad\quad if\ m_i\cdot (k_i')^e(mod\ n) \equiv m_j'\\
&\quad\quad\quad j\ is\ i\\
&攻击思路:由(m,s)group我们可以根据s与s'的关系,计算一个kgroup。\\
&针对于选定的(m',s'),只有与之对应的(m,s)计算的k_{temp}与当初USER选定的盲化因子相同,但其一定存在于我们计算的kgroup.\\
&事实正确确实如此,但是理想协议执行状态下Signer不可获取所有USER的k,因此,我尝试采用m'==m\cdot k_{guess}^e(mod\ n)来判定.\\
&但是结果并没有达到Target.原因如下:\\
&\quad k_{guessi}\equiv s_j'\cdot s_i^{-1}(mod\ n)\rightarrow m_i\cdot k_{guessi}^e(mod\ n)\equiv m_i\cdot (s_j')^e \cdot s_i^{-e}(mod\ n)\equiv m_i\cdot (m_j^d\cdot k_j)^e\cdot m_i^{-1}\equiv m_j\cdot k_j^e(mod\ n)\equiv m'\\
&故m_i\cdot k_{guessi}^e\equiv m'恒成立,攻击失效.
\end{flalign}
\]
from Crypto.Util.number import getPrime, inverse
import random###################### 初始化阶段 ######################
p = getPrime(512); q = getPrime(512)
n = p * q
e = 65537 # RSA加密常用公钥指数
phi = (p - 1) * (q - 1); d = inverse(e, phi)###################### 预处理 ######################
# 设有100名人员参与投票
x = 100
# k_list为每位选票人员盲化时生成的随机数k, k ∈ [1, n - 1]
k_list = [random.randrange(1, n) for _ in range(x)]
# m_list为每为选票人员所投出去的票,为验证结果情况,取值为 1..100
m_list = [i for i in range(1, x + 1)]###################### 签名阶段 ######################
# ss_list 存储选举委员会签名后的 s'; s_list存储最终提交的验证结果; mm_list存储盲化后的 m'
# 至此,我们有了 (m_list, s_list) 与 (mm_list, ss_list),前者对应教材公开后的(m, s); 后者对应选举委员会进行签名时收到的结果
# k_list并非 kgroup; 设 kk_list 表示公开(m,s)后委员会计算的 kgroup
ss_list = []; s_list = []; mm_list = []
for i in range(x):m = m_list[i]; k = k_list[i]# 盲化 mm 代替协议中的 m'mm = m * pow(k, e, n) % n # m' = m * k ^ e (mod n)ss = pow(mm, d, n) # s' = m' ^ d (mod n)s = ss * inverse(k, n) % nmm_list.append(mm); ss_list.append(ss); s_list.append(s)
###################### 验签阶段 ######################
# 若结果为 False ,输出 "Error" 后退出验证
for i in range(x):m = m_list[i]; s = s_list[i]if pow(s, e, n) != m:print("Error")break
###################### 攻击阶段 ######################
print("Attack begin.")
# 我们选取第 51 组的 (m', s'),找到其对应的(m, s)即攻击成功,即若后续结果校验时,在 i = 51 处显示正确,则说明攻击成功
mm = mm_list[51]; ss = ss_list[51]
# 1. 计算 kgroup
kk_list = []
for i in range(x):s = s_list[i]k_guess = ss * inverse(s, n) % n # k_guess = s' * s ^ {-1} (mod n)kk_list.append(k_guess)
# 2. 令 m'' = m * k_guess ^ e (mod n) ,采用 m'' == m' 进行校验
for i in range(x):m = m_list[i]k = kk_list[i]# mmm 表示当前计算的 m'', mm = m * k ^ e (mod n)mmm = m * pow(k, e, n) % nif mmm == mm:print(f"The origin of (m', s') is {i}")
print("Attack over.")
Code
from Crypto.Util.number import getPrime, bytes_to_long
import randomclass TA:def __init__(self):self.p, self.q = [getPrime(1024) for _ in range(2)]self.n = self.p * self.qself.e = 65537phi = (self.p - 1) * (self.q - 1)self.d = pow(self.e, -1, phi)class USER:def __init__(self, TA):self.TA = TAself.k = random.randrange(1, self.TA.n)def blinding(self, message):if isinstance(message, bytes):message = bytes_to_long(message)mm = message * pow(self.k, self.TA.e, self.TA.n) % self.TA.nreturn mmdef unblinding(self, ss):return pow(self.k, -1, self.TA.n) * ssclass Signer:def __init__(self, TA):self.TA = TAdef sign(self, mm):return pow(mm, self.TA.d, self.TA.n)class Verifier:def __init__(self, TA):self.TA = TAdef verify(self, mspair:tuple) -> bool:m, s = mspairif isinstance(message, bytes):m = bytes_to_long(m)return m == pow(s, self.TA.e, self.TA.n)
if __name__ == "__main__":ta = TA()user = USER(ta)signer = Signer(ta)verifier = Verifier(ta)message = b'I choose Alice to go.'mm = user.blinding(message)ss = signer.sign(mm)s = user.unblinding(ss)flag = verifier.verify((message, s))if flag:print("Verify Successfully.")else:print("Error...")
盲签名 -- 部分盲签名 -- 群签名