“not_RSA” in DASCTF

看了大佬的博客才知道是 Paillier cryptosystem,wtcl… 不过还是记录一下自己推导的解题过程

直接使用现成的 Paillier cryptosystem 解密算法解决这题非常容易,分解 n 然后直接套 decrypt 函数就解开了…

题目:

from Crypto.Util.number import getPrime as getprime ,long_to_bytes,bytes_to_long,inverse
from secret import flag,p,q
from sympy import isprime,nextprime
import random

m = bytes_to_long(flag)
n = p * q
g = n + 1
r = random.randint(1, n)

c = (pow(g, m, n * n) * pow(r, n, n * n)) % (n * n)

print('c =', c)
print('n =', n)

主要加密过程是:

$$ \begin{aligned} c&\equiv(g^m \ mod \ n^2)(r^n \ mod \ n^2) \ &(mod \ n^2) \\ &\equiv g^m r^n \ &(mod \ n^2) \end{aligned} $$

其中有

$$ \begin{aligned} g^m&\equiv (n+1)^m \ &(mod \ n^2) \\ &\equiv C_m^0 n^0 + C_m^1 n^1 +C_m^2n^2+…+C_m^mn^m \ &(mod \ n^2) \\ &\equiv C_m^0 n^0 + C_m^1 n^1 \ &(mod \ n^2)\\ &\equiv 1 + mn \ &(mod \ n^2) \end{aligned} $$

所以得到 $c\equiv g^m r^n\equiv (1 + mn)r^n \ (mod \ n^2)$

现在就要想办法消除掉 $r^n$ 的影响,不难发现 $r^n\ mod\ n = c\ mod\ n$。

所以我们需要由 $r^n\ mod\ n$ 得到 $r$ 的值或者 $r^n\ mod\ n^2$的值,才可以对 $r^n$ 在模 $n^2$ 下求逆元。这里我这个菜鸡想了好久…最终想到将 $r^n\ mod\ n$ 分别对 $n$ 的两个因数 $p,q$ 取模,然后再用中国剩余定理(CRT)合并,从而得到 $r$。

然后我们只需要计算 $r^n\ mod\ n^2$ 的逆元并与 $c$ 相乘,就得到 $(1+mn)\ mod\ n^2$,也就得到了 $m$。

from Crypto.Util.number import long_to_bytes, inverse
from functools import reduce

c = 29088911054711509252215615231015162998042579425917914434962376243477176757448053722602422672251758332052330100944900171067962180230120924963561223495629695702541446456981441239486190458125750543542379899722558637306740763104274377031599875275807723323394379557227060332005571272240560453811389162371812183549
n = 6401013954612445818165507289870580041358569258817613282142852881965884799988941535910939664068503367303343695466899335792545332690862283029809823423608093
p = 80006336965345725157774618059504992841841040207998249416678435780577798937819
q = 80006336965345725157774618059504992841841040207998249416678435780577798937447
g = n + 1
phi = (p - 1) * (q - 1)
rn = c % n

x1 = rn % p
d1 = inverse(q, p - 1)
r1 = pow(x1, d1, p)

x2 = rn % q
d2 = inverse(p, q - 1)
r2 = pow(x2, d2, q)


def CRT(m, a):
    Num = len(m)
    M = reduce(lambda x, y: x * y, m)
    Mi = [M // i for i in m]
    t = [inverse(Mi[i], m[i]) for i in range(Num)]
    x = 0
    for i in range(Num):
        x += a[i] * t[i] * Mi[i]
    return x % M


r = CRT([p, q], [r1, r2])

R = pow(r, n, n * n)
R_inv = inverse(R, n * n)
mn = (c * R_inv) % (n * n)
m = (mn - 1) // n
print(long_to_bytes(m))

Paillier Crytposystem

选取素数 $p, q$,计算 $n=p\cdot q$,$\lambda =lcm(p-1,q-1)$,选取 $g\in\Z_{n^2}^*$满足 $g$ 的阶是 $n$ 的倍数。

其中公钥为:$n, g$,私钥为:$p, q,\lambda$。

加密时明文 $m<n$,选取随机的 $r \in \Z_n^*$,计算出密文 $c=g^m r^n \ mod \ n^2$。

解密时的密文 $c<n^2$,明文 $m=\cfrac{L(c^\lambda\ mod\ n^2)}{L(g^\lambda\ mod\ n^2)}\ (mod\ n)$,其中 $L(u)=\cfrac{u-1}{n}$。

在选取合适的 $g$ 的时候,需要判断 $g$ 的阶是否为 $n$ 的倍数,等价于判断 $GCD(L(g^\lambda\ mod\ n^2),n)=1$。

from Crypto.Util.number import *
from gmpy2 import lcm


class Paillier():
    def __init__(self):
        pass

    def encrypt(self, m):
        p, q = getPrime(512), getPrime(512)
        n = p * q
        self.n = n
        assert m < n
        Lcm = lcm(p - 1, q - 1)
        g = getRandomRange(1, n*n)
        while GCD(self.L(pow(g, Lcm, n * n)), n) != 1:
            g = getRandomRange(1, n * n)
        r = getRandomRange(1, n)
        return (pow(g, m, n * n) * pow(r, n, n * n)) % (n * n), p, q, g

    def decrypt(self, c, p, q, g):
        n = p*q
        assert c < n*n
        Lcm = lcm(p - 1, q - 1)
        self.n = n
        self.d = inverse((p - 1) * (q-1), n)
        m_c = self.L(pow(c, Lcm, n * n))
        m_g = self.L(pow(g, Lcm, n * n))
        m = m_c*inverse(m_g, n) % n
        return m

    def L(self, u):
        return (u - 1) // self.n

m = bytes_to_long(b'flag{1234567890}')
P = Paillier()
c, p, q, g = P.encrypt(m)
M = P.decrypt(c, p, q, g)
print(long_to_bytes(M))
# b'flag{1234567890}'

使用 Paillier 解密就可以直接解这一题。

exp:

from Crypto.Util.number import long_to_bytes,inverse
from gmpy2 import lcm
c = 29088911054711509252215615231015162998042579425917914434962376243477176757448053722602422672251758332052330100944900171067962180230120924963561223495629695702541446456981441239486190458125750543542379899722558637306740763104274377031599875275807723323394379557227060332005571272240560453811389162371812183549
n = 6401013954612445818165507289870580041358569258817613282142852881965884799988941535910939664068503367303343695466899335792545332690862283029809823423608093
p = 80006336965345725157774618059504992841841040207998249416678435780577798937819
q = 80006336965345725157774618059504992841841040207998249416678435780577798937447
g = n + 1
phi = (p - 1) * (q - 1)

def decrypt(c, p, q, g):
    n = p * q
    Lcm = lcm(p - 1, q - 1)
    m_c = (pow(c, Lcm, n * n) - 1) // n
    m_g = (pow(g, Lcm, n * n) - 1) // n
    m = m_c * inverse(m_g, n) % n
    return m

m = decrypt(c, p, q, g)
print(long_to_bytes(m))
#b'flag{5785203dbe6e8fd8bdbab860f5718155}'