【RSA】Hastad Broadcast Attackの仕組みと実装

投稿日: 更新日:

使える条件

同じ平文MMを異なるNN、同じeeを使って暗号化された文がee個ある時です。

平文の導出方法

平文をMMee個の公開鍵を(N1,e),(N2,e),...,(Ne,e)(N_1, e), (N_2, e),...,(N_e, e)とし、暗号文をC1,C2,...CeC_1, C_2,...C_eとします。
それぞれの暗号文に対して以下のことが成立します。

C1MemodN1C2MemodN2CeMemodNe\begin{aligned} C_1 &\equiv M^e \mod N_1 \\ C_2 &\equiv M^e \mod N_2 \\ &\vdots \\ C_e &\equiv M^e \mod N_e \end{aligned}

ここで中国剰余定理より

xC1modN1xC2modN2xCemodNe\begin{aligned} x &\equiv C_1 \mod N_1 \\ x &\equiv C_2 \mod N_2 \\ &\vdots \\ x &\equiv C_e \mod N_e \end{aligned}

これを満たすxxN1N2...NeN_1N_2...N_e未満に1つ存在します。
またCiMemodNiC_i \equiv M^e \mod N_iであることより満たすxxMeM^eとなるので

xMemodN1N2...Nex \equiv M^e \mod N_1N_2...N_e

が成立します またM<NiM < N_iよりMe<N1N2...NeM^e < N_1N_2...N_eが成立するので
求める平文MM

M=xeM = \sqrt[e]{x}

となり、求めることができました!

Pythonでの実装

def gcd(a, b):
    #最大公約数を求めます
    if b == 0:
        return a
    else:
        return gcd(b, a%b)

def extgcd(a, b):
    #拡張ユークリッド互除法
    #ax + by = gcd(a, b)となる(x, y)を求めます
    if b == 0:
        return [1, 0]
    if a == 0:
        return [0, 1]

    q = a//b
    r = a%b

    s, t = extgcd(b, r)
    y = s - q*t
    return [t, y]

def crt(rs, ms):
    #中国剰余定理を計算します
    R = 0
    M = 1
    for r, m in zip(rs, ms):
        g = gcd(M, m)

        assert r%g == R%g
        s, _ = extgcd(M, m)

        lcm = m*M // g
        R += (r - R)//g * M * s
        R %= lcm
        M = lcm
    return [R, M]

def e_rooti(a, e):
    # rのe乗根の切り落としを計算します
    ok = 0
    ng = a
    while ng - ok > 1:
        mid = (ok+ng) >> 1
        if mid**e <= a:
            ok = mid
        else:
            ng = mid
    return ok

def hastad_broadcast_attack(e, pairs):
    #e:公開鍵のe, pairs: (c, N)暗号文とNのペアです
    rs, ms = zip(*pairs)
    r, _ = crt(rs, ms)
    return e_rooti(r, e)

練習問題

以下の3つの暗号文から元の平文(数値)を求めてください。
eはすべて3です。

e=3
c1=1467331094246560737
N1=10397589660261129301
c2=853264173255865627
N2=10696384842761712253
c3=10076556659009855575
N3=12180236767128924457

練習問題解答

紹介したPythonのコードを使って解きます。

e=3
paris = [
(1467331094246560737, 10397589660261129301),
(853264173255865627, 10696384842761712253),
(10076556659009855575,12180236767128924457),
]

M = hastad_broadcast_attack(e, pairs)
M = int(M)#小数点切り捨て
print(f"M={M}")

出力

M=1234567890

参考文献

Chennagiri, Abhay. "A Tutorial paper on Hastad Broadcast Attack." http://koclab.cs.ucsb.edu/teaching/cren/project/2017/chennagiri.pdf
(2022/03/03閲覧)

書いた人

profile_image

お茶の葉

物理とプログラミングが好きな人