特殊的数字签名

盲签名

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) % n
    mm_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) % n
    if 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 random

class TA:

    def __init__(self):
        self.p, self.q = [getPrime(1024) for _ in range(2)]
        self.n = self.p * self.q
        self.e = 65537
        phi = (self.p - 1) * (self.q - 1)
        self.d = pow(self.e, -1, phi)

class USER:

    def __init__(self, TA):
        self.TA = TA
        self.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.n
        return mm
    
    def unblinding(self, ss):
        return pow(self.k, -1, self.TA.n) * ss
    
class Signer:

    def __init__(self, TA):
        self.TA = TA

    def sign(self, mm):
        return pow(mm, self.TA.d, self.TA.n)
    
class Verifier:

    def __init__(self, TA):
        self.TA = TA

    def verify(self, mspair:tuple) -> bool:
        m, s = mspair
        if 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...")
posted @ 2025-10-27 20:28  chen_xing  阅读(5)  评论(0)    收藏  举报