검수요청.png검수요청.png

"이산로그"의 두 판 사이의 차이

위키원
이동: 둘러보기, 검색
224번째 줄: 224번째 줄:
 
  return crt(crt_a, crt_m)
 
  return crt(crt_a, crt_m)
 
  a = 7
 
  a = 7
b = 12423425
+
b = 12423425
p = 4766587461926291
+
p = 4766587461926291
x = func4(a,b,p)
+
x = func4(a,b,p)
print(x, pow(a,x,p)==b)
+
print(x, pow(a,x,p)==b)
  
 
{{각주}}
 
{{각주}}

2019년 7월 31일 (수) 16:11 판

이산로그는 일반 로그와 비슷하게 군론에서 정의된 연산으로 1보다 큰 자연수 , 정수 에 대하여 방정식 (mod m) 만족하는 정수 x가 이산로그이다. 이산로그를 계산하는 다항식 시간(polynomial time) 알고리듬이 알려져 있지 않아 이산로그는 현대 암호에 응용되고 있다.

개요

개념

일 때 이다. 실수에서 가 주어지고 를 만족하는 , 즉 를 간단하게 계산할 수 있다. 그러나 에서 주어진 에 대해 를 만족하는 를 찾는 것이 바로 이산로그 문제이다. 이산 로그 문제에서 는 소수, 의 원시근인 것이 좋다.[1]

특징

아직까지 이산 로그 문제에 대한 효율적인 계산법은 나오지 않았다.(여기서 효율적인 계산법이라고 하는 것은 참고로 이산 로그 문제는 NP-complete에 속하는 문제는 아니기에 효율적인 계산법이 나올 가능성은 충분히 있다. 또한 Quantum Computer에서는 P에 속함이 증명되어 있다. 이산 로그는 Elgamal Encryption, Diffie-Hellman 키 교환, Digital Siganature Algorithm 등과 같이 암호화, 키 교환, 인증 등의 각종 암호 분야에서 쓰이고 있다. 만약 이산 로그 문제가 효율적으로 풀리게 된다면 위에서 언급한 암호 시스템이 안전하지 않게 된다. 또한, 비록 일반적인 이산 로그에 대한 풀이법 자체는 아직 찾지 못했다고 하더라도 a,b,p를 적절하게 택하지 못하는 구현의 실수로 인해 기밀성이 지켜지지 않는 경우도 있다. [1]

풀이법

  • 전수조사

이 방법은 가장 떠올리기 쉽고 간단한 풀이법이다. 바로 에 0부터 까지 차례로 넣어보며 를 만족하는 를 찾는 것 이다. 아쉽게도 이 방식은 최악의 경우 번의 곱셈이, 평균적으로는 번의 곱셈이 필요하기 때문에 가 1024 bit 혹은 2048 bit인 보통의 경우에서는 현실적인 시간 내에 풀이가 불가능하다.

def func(a, b, p):
 val = 1
 for x in range(p):
   if val == b: return x
   val = val*a % p
 return -1
  • Baby-step giant-step Algorithm (아기걸음 거인걸음)

알고리즘의 이름은 생소할 수 있지만 일종의 MITM(Meet In The Middle) 알고리즘으로, 이 알고리즘을 통해 시간복잡도를 에서 로 떨어 뜨릴 수 있다. 해쉬를 이용할 경우 , 정렬을 이용할 경우 에 이산 로그 문제를 해결할 수 있다. 정렬을 이용한 예시 코드는 아래와 같다.

def egcd(a1, a2):
 x1, x2 = 1, 0
 y1, y2 = 0, 1
 while a2:
   q = a1 // a2
   a1, a2 = a2, a1 - q * a2
   x1, x2 = x2, x1 - q * x2
   y1, y2 = y2, y1 - q * y2
 return (x1, y1, a1)
def inv(a, m):
 x, y,g = egcd(a, m)
 if g != 1:
   raise Exception('No modular inverse')
 return x%m
def func2(a, b, p):
 table1 = []
 k = int(p**0.5)
 val = 1
 mul = pow(a,k,p)
 for i in range(0,p,k):
   table1.append((val, i//k))
   val = val * mul % p
table2 = []
 ainv = inv(a,p)
 val = b
 for i in range(k):
   table2.append((val, i))
   val = val * ainv % p
   table1.sort()
 table2.sort()
idx1 = 0
 idx2 = 0
 while idx1 < len(table1) and idx2 < len(table2):
   if table1[idx1][0] < table2[idx2][0]:
     idx1 += 1
   elif table1[idx1][0] > table2[idx2][0]:
     idx2 += 1
   else:
     return k*table1[idx1][1]+table2[idx2][1]
 return -1
  • Pollard’s rho Algorithm

Pollard’s rho Algorithm은 인 적절한 를 찾았을 때 이라는 점을 이용하는 알고리즘 이다. 이 알고리즘의 시간복잡도는 입니다. 같은 시간복잡도를 가지는 Baby-step giant-step Algorithm과 비교할 때 이 알고리즘은 확률적 알고리즘이기 때문에 에 반드시 답이 찾아짐을 보장할 수 없고 상수가 약간 크다는 단점이 있지만, 공간을 만 사용한다는 아주 큰 장점이 있습니다. 이 알고리즘은 임의의 꼴의 수들로 수열을 만들었을 때 해당 수열에서 cycle을 찾아내는 방식으로 동작합니다. 수열에서의 cycle은 Floyd’s cycle-finding algorithm으로 구할 수 있습니다. 알고리즘에 대해 간략하게 설명을 하자면, 한 번에 한 칸씩 가는 거북이와 두 칸씩 가는 토끼를 수열 상에서 이동시켜 거북이와 토끼가 같은 값에 위치하는 순간을 찾는 방법입니다. 꼴의 수열을 만들기 위해 를 3으로 나눈 나머지에 따라 각각 혹은 혹은 으로 계산합니다. 반드시 일 필요는 없고 등과 같이 이 여전히 으로 잡아도 무관하다. 그 후 에 대해 앞서 소개한Floyd’s cycle-finding algorithm 으로 를 만족하는 를 찾습니다. 만약 일 경우는 탐색 실패 이므로 다른 초기 항을 잡아 같은 절차를 반복한다. 예시 코드는 아래이다.

def egcd(a1, a2):
 x1, x2 = 1, 0
 y1, y2 = 0, 1
 while a2:
   q = a1 // a2
   a1, a2 = a2, a1 - q * a2
   x1, x2 = x2, x1 - q * x2
   y1, y2 = y2, y1 - q * y2
 return (x1, y1, a1)
def inv(a, m):
 x, y,g = egcd(a, m)
 if g != 1:
   raise Exception('No modular inverse')
 return x%m
def nxt(x, n, m, a, b, p):
 if x % 3 == 0:
   x = a*x%p
   n = (n+1)%(p-1)
 elif x % 3 == 1:
   x = b*x%p
   m = (m+1)%(p-1)
 else:
   x = x*x*x%p
   n = 3*n%(p-1)
   m = 3*m%(p-1)
 return x,n,m
def cycle_detection(a, b, p):
 nrandom = random.randint(0,p-2)
 mrandom = random.randint(0,p-2)
 x_calc = pow(a, nrandom, p)*pow(b,mrandom,p)%p
 x1, n1, m1 = x_calc, nrandom, mrandom
 x2, n2, m2 = x_calc, nrandom, mrandom  
 while(True):
   x1, n1, m1 = nxt(x1, n1, m1, a, b, p)
   x2, n2, m2 = nxt(x2, n2, m2, a, b, p)
   x2, n2, m2 = nxt(x2, n2, m2, a, b, p)
   if x1 == x2:
     try:        
       return (n2-n1)*inv(m1-m2, p-1)%(p-1)
     except:
       return -1           
   def func3(a, b, p):
 while True:
   ret = cycle_detection(a,b,p)
   if ret != -1: return ret
  • Pohlig-Hellman Algorithm

Pohlig-Hellman Algorithm은 위에서 언급한 것과 같이 의 소인수에 대한 주기를 찾아내는 알고리즘이고 이 작은 소인수의 곱으로 나타낼 때는 큰 에 대해 현실적인 시간 내에 계산이 가능해질 수 있다.

def egcd(a1, a2):
 x1, x2 = 1, 0
 y1, y2 = 0, 1
 while a2:
   q = a1 // a2
   a1, a2 = a2, a1 - q * a2
   x1, x2 = x2, x1 - q * x2
   y1, y2 = y2, y1 - q * y2
 return (x1, y1, a1)
def inv(a, m):
 x, y,g = egcd(a, m)
 if g != 1:
   raise Exception('No modular inverse')
 return x%m
def crt(a, m);
n = len(m)
 ret = a[0]
 mod = m[0]
 for i in range(1,n):
   m1 = mod
   mod *= m[i]
   m2inv = inv(m[i],m1)
   m1inv = inv(m1,m[i])
   ret = (ret*m[i]*m2inv+a[i]*m1*m1inv)%mod
 return ret
def func2(a, b, p):
 table1 = []
 k = int(p**0.5)
 val = 1
 mul = pow(a,k,p)
 for i in range(0,p,k):
   table1.append((val, i//k))
   val = val * mul % p
table2 = []
 ainv = inv(a,p)
 val = b
 for i in range(k):
   table2.append((val, i))
   val = val * ainv % p
table1.sort()
 table2.sort()
idx1 = 0
 idx2 = 0
 while idx1 < len(table1) and idx2 < len(table2):
   if table1[idx1][0] < table2[idx2][0]:
     idx1 += 1
   elif table1[idx1][0] > table2[idx2][0]:
     idx2 += 1
   else:
     return k*table1[idx1][1]+table2[idx2][1]
 return -1
def MITM(a, b, mod, pp):
 table1 = []
 k = int(pp**0.5)
 val = 1
 mul = pow(a,k,mod)
 for i in range(0,pp,k):
   table1.append((val, i//k))
   val = val * mul % mod
table2 = []
 ainv = inv(a,mod)
 val = b
 for i in range(k):
   table2.append((val, i))
   val = val * ainv % mod
table1.sort()
 table2.sort()
idx1 = 0
 idx2 = 0
 while idx1 < len(table1) and idx2 < len(table2):
   if table1[idx1][0] < table2[idx2][0]:
     idx1 += 1
   elif table1[idx1][0] > table2[idx2][0]:
     idx2 += 1
   else:
     return k*table1[idx1][1]+table2[idx2][1]
 return -1
def func4(a, b, p):
 factors = []
 i = 2
 tmp = p-1
 while tmp >= i*i:
   if tmp % i != 0:
     i += 1
     continue
   cnt = 0
   while tmp % i == 0:
     tmp //= i
     cnt += 1
   factors.append((i, cnt))
   i += 1
 if tmp != 1:
   factors.append((tmp, 1))
crt_a = []
 crt_m = []  
 for factor in factors:
   pp, ee = factor
   cura = pow(a, (p-1)//(pp**ee), p)
   curb = pow(b, (p-1)//(pp**ee), p)
   gamma = pow(cura, pp**(ee-1), p)
   exp = 0
   for i in range(ee):
     b_tmp = inv(pow(cura, exp, p), p) * curb % p
     b_tmp = pow(b_tmp, (pp**(ee-1-i)), p)
     # Want to find gamma ** x = b_tmp (mod p), x in (0,1,2,...,pp-1)
     exp += MITM(gamma, b_tmp, p, pp)*pp**i
crt_a.append(exp)
   crt_m.append(pp**ee)
return crt(crt_a, crt_m)
a = 7
b = 12423425
p = 4766587461926291
x = func4(a,b,p)
print(x, pow(a,x,p)==b)

각주

  1. 1.0 1.1 이산-로그〉, 《secmem》

같이보기


  검수요청.png검수요청.png 이 이산로그 문서는 암호 알고리즘에 관한 글로서 검토가 필요합니다. 위키 문서는 누구든지 자유롭게 편집할 수 있습니다. [편집]을 눌러 문서 내용을 검토·수정해 주세요.