서론
이 글은 이전에 작성한 Tonelli-Shanks 구현 포스팅에 Tonelli-Shanks에 대한 이론적 내용을 덧붙여 작성한 글입니다. 배우며 작성한 글이므로 틀린 내용이 있을 수 있으니, 발견하시면 댓글로 알려주시길 바랍니다.
본 글은 https://rkm0959.tistory.com/20를 토대로 학습하며 작성한 글입니다.
암호학을 공부하다 보면 Z/pZ에서 이차방정식을 풀어야 할 필요가 있다. CTF에서 RSA 문제를 풀 때 m2modp를 구했다거나, Diffie-Hellman 프로토콜 문제를 풀며 g2modp로부터 g를 찾아야 한다거나… 여러 상황에서 이차방정식을 해결할 수 있다면 편리할 것이다.
우선 Z/pZ가 아니라 R에서 이차방정식을 풀 때를 생각해보자. 근의 공식이 너무 익숙하지만, 그 이전에는 한 변을 완전제곱식으로 변형하고 양변에 제곱근을 취하여 해를 구하였다.
따라서 Z/pZ에서도 x2≡n (mod p)를 풀 수 있다면, 임의의 이차방정식에 대해 근을 구할 수 있음을 알 수 있다. 이러한 해결책으로는 Tonelli-Shanks 알고리즘을 사용하는 시간복잡도 O(log2p)의 솔루션이 존재한다.
우선 다음과 같은 Lemma를 확인해두자.
Lemma 1
modp에서 quadratic residue는 정확히 p−12개 존재한다.
Proof
우선, 1≤k≤p−12일 때 k2≡(p−k)2 (mod p)이다.
1≤a<b≤p−12이라고 하자. a2−b2≡(a+b)(a−b) (mod p)인데, 2≤a+b≤p−1이고 a와 b가 다른 수이므로 a2−b2modp는 0이 아니다. 따라서 1,2,⋯,p−12는 모두 제곱한 값이 서로 다르며, quadratic residue는 정확히 p−12개 있음을 알 수 있다.
또한, Lemma 1에 의해 자명하게 quadratic non-residue 역시 p−12개이다.
Lemma 2
n이 modp에서 quadratic residue ⇔ np−12≡1 (mod p)
Proof
quadratic residue라면 x2≡n (mod p)인 x가 존재하고, np−12≡xp−1≡1 (mod p)이다. xp−1≡1 (mod p)는 페르마 소정리에 의해 성립한다.
np−12≡1 (mod p)는 p−12차 방정식이므로 최대 p−12개의 근을 갖는다. 그러므로 quadratic non-residue는 이 방정식의 근이 될 수 없다.
참고로 n이 quadratic non-residue일 때 np−12≡−1 (mod p)이다. 1≡np−1≡(np−12)2 (mod p)이므로 np−12≡±1 (mod p)인데, np−12≡1 (mod p)은 아니므로 np−12≡−1 (mod p)이다.
Lemma 3
√p 이하의 자연수 중 quadratic non-residue가 적어도 하나 존재한다.
Proof
√p 이하의 자연수가 모두 quadratic residue라고 가정하자. 이때 quadratic residue의 집합을 Q라고 하면, a,b∈Q일 때 ab∈Q이다. 따라서 x∈Z/pZ인 임의의 x에 대해 x∈Q를 보장할 수 있다. 하지만 Lemma 2에 의해 quadratic residue는 p−12개뿐이므로 모순이 발생한다.
즉, 귀류법에 의해 Lemma 3이 성립한다.
Tonelli-Shanks 알고리즘
Tonelli-Shanks 알고리즘은 다음과 같이 진행된다.
-
n이 mod p에서 quadratic residue인지 확인한다. (Lemma 2 활용)
-
p−1=Q⋅2S인 홀수 Q와 음 아닌 정수 S를 구한다.
-
mod p에서 quadratic non-residue인 정수 z를 하나 찾는다. Lemma 2에 의해 조사해야 하는 수의 개수의 기댓값이 2임을 알 수 있다.
최악의 경우에도 Lemma 3에 의해 O(√plogp)에 찾을 수 있으나, 보통은 거의 상수 시간복잡도로 찾을 수 있다.
-
다음과 같이 정의하자.
- M≡S (mod p)
- c≡zQ (mod p)
- t≡nQ (mod p)
- R≡nQ+12 (mod p)
-
다음 과정을 반복한다.
- t=0이면 0을 출력한다.
- t=1이면 R을 출력한다.
- 0<i<M과 t2i≡1 (mod p)를 만족하는 최소의 자연수 i를 찾는다.
- b를 b≡c2M−i−1 (mod p)로 정의한다.
- M←i, c←b2, t←tb2, R←Rb (mod p)
-
출력된 값이 r이라면 ±r이 x2≡n (mod p)의 두 근이다.
알고리즘의 타당성 증명
초기의 정의를 다시 한 번 봐보자.
- M=S
- c≡zQ (mod p)
- t≡nQ (mod p)
- R≡nQ+12 (mod p)
이때 초기값에 관해 다음과 같은 사실을 알 수 있다. ⋯ (A)
- z가 quadratic non-residue이므로 c2M−1≡zQ⋅2S−1≡zp−12≡−1 (mod p)이다.
- n은 quadratic residue이므로 t2M−1=nQ⋅2S−1≡np−12≡1 (mod p)이다.
- R2≡nQ+1≡nt (mod p)이다.
(5.)에서 M,c,t,R에 각각 M′,c′,t′,R′이 대입된다고 하자.
각각의 값이 어떻게 정의되는지 다시 한 번 확인하자.
- b≡c2M−i−1 (mod p)
- M′는 0<M′<M과 t2M′≡1 (mod p)를 만족하는 최소의 자연수
- c′≡b2 (mod p)
- t′≡tb2 (mod p)
- R′≡Rb (mod p)
b의 정의에 따라 다음과 같은 명제가 성립한다.
-
c′2M′−1≡b2i≡c2M−1≡−1 (mod p)
-
t′2M′−1≡t2M′−1b2M′≡(−1)⋅(−1)≡1 (mod p)
-
R′2≡R2b2≡ntb2≡nt′ (mod p)
따라서 (A)에서 성립한 식은 (5.)를 몇 번 반복하더라도 여전히 성립함을 알 수 있다.
이 때 t2M−1≡1 (mod p)이므로 0<M′<M인 M′은 항상 존재하며, M의 값이 항상 감소하게 됨을 알 수 있다. 그렇게 계속 반복하며 M=1이 되는 순간 1≡t2M−1≡t1 (mod p)이므로 t≡1 (mod p)임이 보장되며, R2≡nt≡n (mod p)이므로 R은 x2≡n (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))