Partial Key Exposure Attack

RSA秘密鍵dの下位bitがある程度わかっているときに使える

nのbit数の半分程度のbit数がわかっていて、 pとqが近い時に成功したりしなかったりする。MultiPrimeだともうちょっとbit数がいるっぽい。3/4くらい?

from Crypto.Util.number import getPrime, inverse
from math import gcd
import random

e = 1667
p = getPrime(1024)
q = getPrime(1024)
n = p * q
d = inverse(e, (p - 1) * (q - 1))


c1 = pow(2, e, n)
c2 = pow(3, e, n)

low_bits = 1024
low_d = d % (2 ** low_bits)

print("size of n: {}".format(n.bit_length()))
print("size of low_d: {}".format(low_bits))
for k in range(1, e + 1):
    if k % 100 == 0:
        print(k)
    high_d = (k * n + 1) // e
    high_d >>= low_bits
    high_d <<= low_bits
    approx_d = high_d | low_d

    if pow(c1, approx_d, n) == 2 and pow(c2, approx_d, n) == 3:
        print("FOUND! d={}".format(approx_d))
        print(approx_d == d)
        break

eが小さくてnのbit数の1/4程度のdがわかっている時

def partial_key_exposure(d_, e, n):
    beta = 0.5
    epsilon = beta^2/7
    n_bits = n.nbits()
    d_bits = floor(n_bits*(beta^2+epsilon))
    d_ = d_ % (2^d_bits)

    P = var('P')
    for k in xrange(1, e+1):
        print(k)
        answers = solve_mod([e*d_*P - k*P*(n-P+1) + k*n == P], 2^d_bits)
        for ans in answers:
            p_ = ZZ(ans[0])
            PR.<x> = PolynomialRing(Zmod(n))

            f = 2^d_bits*x + p_
            f = f.monic()
            roots = f.small_roots(X=2^(n_bits//2-d_bits), beta=beta, epsilon=epsilon)  # find root < 2^(nbits//2-d_bits) with factor >= n^0.3
            if roots:
                root = roots[0]
                if root == 0:
                    continue
                p = gcd(2^d_bits*root + p_, n)
                return Integer(p)