이전된 블로그의 글로 이동하기

서론

이 글은 이전에 작성한 Tonelli-Shanks 구현 포스팅에 Tonelli-Shanks에 대한 이론적 내용을 덧붙여 작성한 글입니다. 배우며 작성한 글이므로 틀린 내용이 있을 수 있으니, 발견하시면 댓글로 알려주시길 바랍니다.

본 글은 https://rkm0959.tistory.com/20를 토대로 학습하며 작성한 글입니다.

암호학을 공부하다 보면 Z/pZ에서 이차방정식을 풀어야 할 필요가 있다. CTF에서 RSA 문제를 풀 때 m2modp를 구했다거나, Diffie-Hellman 프로토콜 문제를 풀며 g2modp로부터 g를 찾아야 한다거나… 여러 상황에서 이차방정식을 해결할 수 있다면 편리할 것이다.

우선 Z/pZ가 아니라 R에서 이차방정식을 풀 때를 생각해보자. 근의 공식이 너무 익숙하지만, 그 이전에는 한 변을 완전제곱식으로 변형하고 양변에 제곱근을 취하여 해를 구하였다.

따라서 Z/pZ에서도 x2n (mod p)를 풀 수 있다면, 임의의 이차방정식에 대해 근을 구할 수 있음을 알 수 있다. 이러한 해결책으로는 Tonelli-Shanks 알고리즘을 사용하는 시간복잡도 O(log2p)의 솔루션이 존재한다.

우선 다음과 같은 Lemma를 확인해두자.

Lemma 1

modp에서 quadratic residue는 정확히 p12개 존재한다.

Proof

우선, 1kp12일 때 k2(pk)2 (mod p)이다.

1a<bp12이라고 하자. a2b2(a+b)(ab) (mod p)인데, 2a+bp1이고 ab가 다른 수이므로 a2b2modp는 0이 아니다. 따라서 1,2,,p12는 모두 제곱한 값이 서로 다르며, quadratic residue는 정확히 p12개 있음을 알 수 있다.

또한, Lemma 1에 의해 자명하게 quadratic non-residue 역시 p12개이다.

Lemma 2

nmodp에서 quadratic residue np121 (mod p)

Proof

quadratic residue라면 x2n (mod p)x가 존재하고, np12xp11 (mod p)이다. xp11 (mod p)페르마 소정리에 의해 성립한다.

np121 (mod p)p12차 방정식이므로 최대 p12개의 근을 갖는다. 그러므로 quadratic non-residue는 이 방정식의 근이 될 수 없다.

참고로 n이 quadratic non-residue일 때 np121 (mod p)이다. 1np1(np12)2 (mod p)이므로 np12±1 (mod p)인데, np121 (mod p)은 아니므로 np121 (mod p)이다.

Lemma 3

p 이하의 자연수 중 quadratic non-residue가 적어도 하나 존재한다.

Proof

p 이하의 자연수가 모두 quadratic residue라고 가정하자. 이때 quadratic residue의 집합을 Q라고 하면, a,bQ일 때 abQ이다. 따라서 xZ/pZ인 임의의 x에 대해 xQ를 보장할 수 있다. 하지만 Lemma 2에 의해 quadratic residue는 p12개뿐이므로 모순이 발생한다.

즉, 귀류법에 의해 Lemma 3이 성립한다.

Tonelli-Shanks 알고리즘

Tonelli-Shanks 알고리즘은 다음과 같이 진행된다.

  1. nmod p에서 quadratic residue인지 확인한다. (Lemma 2 활용)

  2. p1=Q2S인 홀수 Q와 음 아닌 정수 S를 구한다.

  3. mod p에서 quadratic non-residue인 정수 z를 하나 찾는다. Lemma 2에 의해 조사해야 하는 수의 개수의 기댓값이 2임을 알 수 있다.

    최악의 경우에도 Lemma 3에 의해 O(plogp)에 찾을 수 있으나, 보통은 거의 상수 시간복잡도로 찾을 수 있다.

  4. 다음과 같이 정의하자.

    • MS (mod p)
    • czQ (mod p)
    • tnQ (mod p)
    • RnQ+12 (mod p)
  5. 다음 과정을 반복한다.

    • t=0이면 0을 출력한다.
    • t=1이면 R을 출력한다.
    • 0<i<Mt2i1 (mod p)를 만족하는 최소의 자연수 i를 찾는다.
    • bbc2Mi1 (mod p)로 정의한다.
    • Mi, cb2, ttb2, RRb (mod p)
  6. 출력된 값이 r이라면 ±rx2n (mod p)의 두 근이다.

알고리즘의 타당성 증명

초기의 정의를 다시 한 번 봐보자.

  • M=S
  • czQ (mod p)
  • tnQ (mod p)
  • RnQ+12 (mod p)

이때 초기값에 관해 다음과 같은 사실을 알 수 있다. (A)

  • z가 quadratic non-residue이므로 c2M1zQ2S1zp121 (mod p)이다.
  • n은 quadratic residue이므로 t2M1=nQ2S1np121 (mod p)이다.
  • R2nQ+1nt (mod p)이다.

(5.)에서 M,c,t,R에 각각 M,c,t,R이 대입된다고 하자.

각각의 값이 어떻게 정의되는지 다시 한 번 확인하자.

  • bc2Mi1 (mod p)
  • M0<M<Mt2M1 (mod p)를 만족하는 최소의 자연수
  • cb2 (mod p)
  • ttb2 (mod p)
  • RRb (mod p)

b의 정의에 따라 다음과 같은 명제가 성립한다.

  • c2M1b2ic2M11 (mod p)

  • t2M1t2M1b2M(1)(1)1 (mod p)

  • R2R2b2ntb2nt (mod p)

따라서 (A)에서 성립한 식은 (5.)를 몇 번 반복하더라도 여전히 성립함을 알 수 있다.

이 때 t2M11 (mod p)이므로 0<M<MM은 항상 존재하며, M의 값이 항상 감소하게 됨을 알 수 있다. 그렇게 계속 반복하며 M=1이 되는 순간 1t2M1t1 (mod p)이므로 t1 (mod p)임이 보장되며, R2ntn (mod p)이므로 Rx2n (mod p)의 해가 된다.

알고리즘의 시간복잡도 증명

(1.), (2.), (4.)는 자명하게 O(logp)에 동작함을 알 수 있다.

(3.) 역시 보통 상수 시간복잡도로 찾을 수 있다.

(5.)에서 i를 찾는 과정은 O(M)인데, M은 최악의 경우 O(logp)이다. 따라서 O(logp) 이내에 찾을 수 있다. (5.)의 이외의 과정도 O(logp)에 계산할 수 있다.

(5.)는 최대 O(logp)번 반복되므로 총 O(log2p)에 동작한다.

따라서 전체 시간복잡도는 O(logp+1+log2p)=O(log2p)가 됨을 알 수 있다.

Python 구현

def tonelli_shanks(n,p):
    def isQR(x,p):# check whether x is quadratic residue
        return pow(x,(p-1)//2,p)==1
    # (1.)
    if not isQR(n,p):
        return -1

    # (2.)
    Q,S=p-1,0
    while Q%2==0:
        S+=1
        Q//=2

    # (3.)
    z=None
    for x in range(2,p):
        if not isQR(x,p):
            z=x
            break
    # (4.)
    M,c,t,R=S,pow(z,Q,p),pow(n,Q,p),pow(n,(Q+1)//2,p)

    # (5.)
    while True:
        if t==0:
            return 0
        elif t==1:
            return R
        k=t*t%p
        ii=None
        for i in range(1,M):
            if k==1:
                ii=i
                break
            k*=k
            k%=p
        b=pow(c,2**(M-i-1),p)%p
        M=ii%p
        c=b*b%p
        t=t*c%p
        R=R*b%p

# test
if __name__=='__main__':
    n,p=map(int,input().split())
    print("STARTED")
    print(tonelli_shanks(n,p))
yubin.choi's profile image

최유빈(yubin.choi)

2021-08-30 20:00

Read more posts by this author