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

이산로그

해시넷
이동: 둘러보기, 검색

이산로그(discrete logarithms)는 일반 로그와 비슷하게 군론에서 정의된 연산으로, 1보다 큰 자연수 , , 정수 에 대하여 방정식 를 만족하는 정수 를 가리킨다. 이산로그를 계산하는 다항식 시간(polynomial time) 알고리즘이 알려져 있지 않아 이산로그는 현대 암호에 응용되고 있다.[1] 이산대수(離散對數)라고도 한다.[2]

개념[편집]

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

이산대수의 역함수인 이산 거듭제곱은 효율적으로 계산할 수 있지만, 이산대수 계산은 어려운 것으로 알려진 군이 존재한다. (이산 거듭제곱의 경우 제곱을 여러번 반복해서 효율적으로 계산하는 방법등이 있다.) 일부 암호는 이런 비대칭성을 바탕으로 설계되기도 한다. 일반 이산대수를 구하는 문제는 일반 이산대수 문제(generalized discrete logarithm problem, 혹은 줄여서GDLP)라고 부르며, 현재까지 이에 대한 효율적인 알고리즘을 찾지 못하고 있다. 암호학에서 는 보통 순환군 를 사용한다. 최근에는 유한체에서 정의된 타원 곡선의 순환 부분군을 암호에 적용하기 위해 많은 연구가 진행되고 있다.[4]

이산대수의 가장 단순한 형태는 에서 정의하는 것이다. 의 집합은 이고 소수 를 법으로 가지는 모듈로 곱셈에 대하여 닫혀있다. 이 군에서 어떤 수의 제곱을 구하려면, 그 수를 번 제곱한 다음 로 나눈 나머지를 구하면 된다. 이런 연산을 이산 거듭제곱(discrete exponentiation)이라고 한다.[4] 예를 들어, 에서 을 구하는 경우, 이고 의 나머지가 이므로 에서 이다. 이산대수는 이산 거듭제곱의 역함수이다. 앞의 예제를 사용하여 설명하면, 일 때 이 조건을 만족시키는 가장 작은 에서 밑이 의 이산대숫값이다. 이산로그문제(discrete logarithm problem)라고도 한다. 이산대수의 정의를 일반화하면, 개의 원소를 가진 유한 순환군이라고 하자. 이 군은 곱셈군이라고 가정한다. 의 생성원(primitive root, primitive element)이면 의 모든 원소 는 임의의 정수 에 대하여 의 형태로 쓸 수 있다. 또한 를 표현하는 모든 원소들은 모듈로 n에 대해 합동이다. 이런 조건에서 다음과 같은 함수를 정의한다.

여기서 은 정수 을 법으로 가지는 환이고 에는 모듈로 에 대한 congruence class를 할당한다. 이와 같은 군 동형사상을 밑이 인 이산대수라고 부른다. 로그 함수의 밑변환은 이산대수에서도 그대로 적용된다. 의 또다른 생성원이면, 다음 식이 성립한다. [5]

특징[편집]

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

Safe Prime

Pohlig-Hellman Algorithm에서 볼 수 있듯 의 소인수 중 그다지 큰 것이 없으면 이산로그를 굉장히 쉽게 구할 수 있다. 이를 막기 위해 를 safe prime으로 택하는 것이 좋다. safe prime은 꼴의 소수를 의미합니다.2048-bit의 safe prime을 택하기 위해서는 1024-bit의 prime 를 임의로 택한다. 그리고 이 소수인지 확인하는 방법으로 만들어낼 수 있다. 이 소수일 확률은 소수 정리에 의해 에 근사하기 때문에 대략 2048개의 를 잡으면 safe prime을 만들어낼 수 있다.[3]

이산대수 문제[편집]

이산대수 문제(Discrete Logarithm Problem, DLP)는 군(group)을 이용한다. 일반적으로 차수가 인 집합 의 원소 와 집합 의 다른 원소가 주어졌을 때 를 구하는 것은 어려우며 이를 이산대수 문제(DLP: Discrete Logarithm Problem)라 한다. 여기서 이다. 번 곱한 결과이다. 원소 는 전형적으로 의 모든 원소들을 생성하거나 또는 적어도 에서 사이의 모든 정수들을 가지고 누승함으로서(즉, 반복적으로 그룹 연산을 함으로서)큰 부분집합을 생성할 수 있다. 원소 가 군 내의 모든 원소들을 생성할 수 있다면 원소 를 생성자(generator)라고 한다. 유한체의 이산대수문제는 유한체의 곱셈군에 대한 이산대수문제이다. 소인수분해 문제와 마찬가지로 이산대수 문제는 일방향 함수의 어려운 문제라 여겨지고 있다. 이러한 이유로 이산대수 문제는 엘가말(ElGamal) 시스템과 DSS를 포함한 여러 공개키 암호 시스템들의 기초를 이루어 왔다. 이들 시스템들의 안전성은 이산대수를 계산하기가 어렵다는 가정에 의존하고 있다. 이산대수 문제는 유한체(finite fields)상에서 보다 임의의 군(group)상에서 훨씬 어려운 듯하다. 즉, 이것은 타원 곡선(elliptic curve)군에 기초한 암호 시스템의 동기 부여이다.[6]

알고리즘[편집]

  • 전수조사

이 방법은 가장 떠올리기 쉽고 간단한 풀이법이다. 바로 에 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) [3]

활용[편집]

엘가말[편집]

엘가말(EIGamal)은 1985년 이산대수 문제에 바탕을 둔 공개키 암호 시스템이다. 아래는 엘가말 암호 알고리즘을 이용하여 송신인 갑이 수신인 을에게 메시지를 전달하는 절차를 설명한 것이다.

  • 준비과정
  1. 차수가 소수 인 순환군 와 이의 한 생성원 를 정한다.
  2. 을은 정수 를 선택한다. ()
  3. 을 계산한다.
  4. 가 공개 키가 된다.
  • 암호화
  1. 갑은 을의 공개 키를 전달받는다.
  2. 갑은 을에게 전달할 메시지 을 선택한다.
  3. 갑은 무작위로 정수 를 선택한다. ()
  4. 계산한다.
  5. 갑이 을에게 암호문 를 전달한다.
  • 복호화
  1. 을은을 계산한다. (이는 와 그 값이 동일하다.)
  2. 을이 을 계산하면 갑이 전달하고자 했던 메시지 이 나온다.[7]

디피 헬만[편집]

디피 헬만 알고리즘(Diffie-Hellman Algorithm)은 상대방의 공개키와 나의 개인키를 이용하여 계산을 하면 비밀키가 나온다. 그 후 나와 상대방은 비밀키를 사용하여 데이터를 암호화한 후 전달하면 된다. 이러한 DH 알고리즘은 '키 교환(key exchange)' 알고리즘으로 대칭키를 공유하는데 사용한다. 이는 암호화나 서명을 위한 것이 아니며, 이산대수 문제(Discrete Logarithm Problem)(혹은 이산로그)라는 방식을 이용하는데 일때 를 안다면 는 구하기 쉽지만 를 알땐 를 구하기는 어렵다는 방식에 착안하여 만들어진 알고리즘이다.

  • 동작원리
공개적으로 교환할 발생기(Generator)를 생성한다. 이는 라고 하고 우리가 할 값 는 소수로 지정한다.
  1. A는 개인키 a를 이용하여 를 생성한다.
  2. B는 개인키 b를 이용하여 를 생성한다.
  3. A는 B에게 를 보내고 B는 A에게 를 보낸다.
  4. A는 자신의 개인키 a를 이용하여 를 만들고
  5. B는 자신의 개인키 b를 이용하여 를 만든다.
  6. A와 B는 새롭게 생성된 키를 대칭키(비밀키)로 이용한다.
이때 공격자인 T가 있다고 가정해보면 T는 A와 B사이에서 데이터를 가로채기 시작하는데 공격자가 가로챈 값들로는 절대 를 만들 수 없음을 알 수 있다.
  • 취약점
디피 헬만 알고리즘은 대칭키를 비밀스럽게 만들 수 있다는 점에서 장점이 있지만, T가 값을 조작해버리면 중간자 공격(Man In the Middle attack, MIM)에 당하게 된다.
  1. A와 B가 서로 g를 이용하여 를 보낸다.
  2. 중간에서 T가 t라는 개인키로 서로에게 보내고 T는 를 이용하여 를 만든다.
  3. A와 B는 눈치채지 못하고 t가 준 를 이용하여 서로 를 만든다.
  4. 이제 A, B, T 모두 같은 대칭키를 가지게 되어 공격을 당할 수 있게 되었다.[8]

암호학[편집]

암호학에서는 이산로그의 효율적 알고리즘이 존재하지는 않는다. 하지만 그 반대 방향인 거듭제곱 연산은 간단히 계산할 수 있다는 일방항 함수의 아이디어를 RSA에서와 마찬가지로 이용한 것이다. 만약 이산로그 문제를 계산할 수 있다면 원래 비밀번호를 알아낼 수 있다. 이 때문에 이산로그의 비효율성은 보안성에 중요 역할을 한다. 엘가말 암호와 전자 서명 알고리즘 등의 암호 체계에서도 비슷한 방법을 이용한다. 타원 곡선 암호에서는 유한체에서 정의된 타원 곡선의 순환 부분군의 이산로그 문제를 활용한다.[1]

타원곡선암호[편집]

타원곡선
들의 그래프
  • 정의
일반적으로 타원곡선 방정식은 다음과 같은 형태의 3차 방정식을 이용한다.
(식 1)  

그러나, 실수상의 타원 곡선은 다음과 같은 특별한 범주에 속하는 타원 곡선을 사용한다.

(식 2)  

타원곡선은 일반적으로 타원 형태의 그래프를 생각하기 쉬우나, 타원곡선은 우측의 그림과 같은 형태의 곡선을 가지며 (식 2)의 의 값에 따라 다양한 형태의 타원 곡선을 정의할 수 있다. 타원곡선의 특징은 축을 중심으로 대칭되며, 비 수직선에 대해 최대 3개 지점에서 곡선과 교차한다는 점이다.

타원곡선의 이산대수 문제에 기반한 타원곡선 암호화[편집]

타원곡선 덧셈 그래프
  • 타원곡선암호의 원리 및 키생성
타원곡선암호는 타원곡선 위의 이산로그 문제가 어렵다는 사실을 이용한 공개키 암호 방식이다. 타원곡선 암호화를 위한 타원곡선은 임의의 정수 , 에 대해 정의된 다음과 같은 방정식의 해 의 집합이다.
(식 3)  

가 타원곡선 상에 있다는 것은 위 방정식을 만족시킨다는 뜻이고, 두 점 , 와 임의의 정수 에 대해 다음 방정식을 정의할 수 있다.

(식 4)  

이때 해 를 구하는 것이 타원곡선 이산대수 문제이다. 이로부터 타원곡선암호에서 사용하는 키 쌍은 다음과 같이 정의할 수 있다.

 : 생성자, 임의의 시작 포인트
 : 개인키,  보다 적은 소수(Prime)로, 난수 생성기로 생성
 : 공개키, 개인키로부터 연산

이때 공개키 (번 덧셈)으로 번 덧셈연산한 값이다. 수식에서 를 이용해서 를 구하기는 쉽지만, 를 안다고 해서, 값을 유추해 내기가 굉장히 어려운 타원곡선 이산대수 문제를 이용한다. 는 타원 곡선상의 임의의 점이며 를 타원곡선상에서 번 덧셈 연산한 것을 의미한다. 는 점 에서의 접선이 타원곡선과 만나는 제 3의 점을 축으로 대칭시킨 지점이다. 에 해당하는 점에서 마찬가지로 접선을 그어 타원 곡선과 만나는 점의 축 대칭 점이다. 의 상수 배 연산은 이를 반복적으로 수행하여 표현할 수 있으며, 타원곡선상에서 이루어지는 특성을 보인다. 타원 곡선은 공개키 암호 체계를 수학적으로 수행하는 한 가지 방법으로써 타원곡선을 이용하여, RSA, 엘가말, 디피 헬만을 구현할 수 있다.

엘가말 공개키 암호 타원곡선 공개키 암호
공개키
개인키
암호화
복호화
(는 임의의 난수이다.)[9]

각주[편집]

  1. 1.0 1.1 이산_로그〉, 《위키백과》
  2. 로그(logarithm)를 대수(對數)라 부르기도 하므로, 이산대수와 이산로그는 같은 말이다.
  3. 3.0 3.1 3.2 3.3 이산-로그〉, 《secmem》
  4. 4.0 4.1 꼬막, 〈이산대수-위키〉, 《네이버 블로그》, 2008-06-11
  5. 이산 로그〉, 《위키백과》
  6. Fist1stPark, 〈유한체의 이산대수〉, 《티스토리》, 2009-04-03
  7. 엘가말 암호〉, 《위키백과》
  8. Crocus, 〈디피 헬만 알고리즘(Diffie-Hellman Algorithm)〉, 《티스토리》, 2018-04-22
  9. 김미영, 〈이제는 알 때가 됐다! – ECC 알고리즘〉, 《아이리포》, 2018

참고자료[편집]

같이 보기[편집]


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