-
[SISS/암호학 스터디] 여름 7주차 스터디 - 디피헬만 키 교환24-여름 SISS/암호학 2024. 8. 16. 21:35
여름 7주차 스터디 - 디피헬만 키 교환 수강 인증 화면 캡쳐 : 7주차 08/12 ~ 08/18 키 교환 알고리즘
키 교환 알고리즘
- 대칭키 암호 → 송신자와 수신자가 같은 키를 공유하고 있어야 함
- 디피-헬만 알고리즘
- 이산 로그 문제 → a^x ≡ b (mod m)을 만족하는 x를 구하는 것이 어렵다는 점을 이용 (m^(1/2)번 정도의 연산을 해아함)
- 쉽게 해결되는 경우 → p-1에 대해 smooth할 때 (소인수가 모두 p-1보다 작을 때/p는 소수 중 p+1, p-1이 작은 소수로 소인수분해되는 수)
- 키 교환 과정 → K가 키로 사용됨
- 앨리스: 소수 p와 1 ≤ g ≤ p−1인 g를 밥에게 전송
- 밥: 1 ≤ b ≤ p−1인 b를 정하여 B = g^(ba) mod p를 앨리스에게 전송
- 앨리스: 밥이 보낸 B를 a제곱하여 K ≡ B^a ≡ (g^b)^a ≡ g^(ba) mod p를 계산
- 앨리스: 1 ≤ a ≤ p−1인 a를 정하여 A = g^a mod p를 밥에게 전송
- 밥: 앨리스가 보낸 A를 b제곱하여 K ≡ A^b ≡ (g^a)^b **≡ g^(ab) mod p를 계산
- 중간자 공격
- 통신하는 상대가 누구인지 모른다는 점을 이용해 공격자가 송수신자의 가운데에서 두 사람과 각각 키를 생성중간자 공격
- 이산 로그 문제 → a^x ≡ b (mod m)을 만족하는 x를 구하는 것이 어렵다는 점을 이용 (m^(1/2)번 정도의 연산을 해아함)
중간자 공격 실습 - 중간자 공격
- challenge.py → 공격자에 대한 검증이 없음
#!/usr/bin/python3 from Crypto.Util.number import getPrime from Crypto.Util.Padding import pad, unpad from Crypto.Cipher import AES import hashlib import random class Person(object): def __init__(self, p): self.p = p self.g = 2 self.x = random.randint(2, self.p - 1) def calc_key(self): self.k = pow(self.g, self.x, self.p) return self.k def set_shared_key(self, k): self.sk = pow(k, self.x, self.p) aes_key = hashlib.md5(str(self.sk).encode()).digest() self.cipher = AES.new(aes_key, AES.MODE_ECB) def encrypt(self, pt): return self.cipher.encrypt(pad(pt, 16)).hex() def decrypt(self, ct): return unpad(self.cipher.decrypt(bytes.fromhex(ct)), 16) flag = open("flag", "r").read().encode() prime = getPrime(1024) print(f"Prime: {hex(prime)}") alice = Person(prime) bob = Person(prime) alice_k = alice.calc_key() print(f"Alice sends her key to Bob. Key: {hex(alice_k)}") print("Let's inturrupt !") alice_k = int(input(">> ")) if alice_k == alice.g: exit("Malicious key !!") bob.set_shared_key(alice_k) bob_k = bob.calc_key() print(f"Bob sends his key to Alice. Key: {hex(bob_k)}") print("Let's inturrupt !") bob_k = int(input(">> ")) if bob_k == bob.g: exit("Malicious key !!") alice.set_shared_key(bob_k) print("They are sharing the part of flag") print(f"Alice: {alice.encrypt(flag[:len(flag) // 2])}") print(f"Bob: {bob.encrypt(flag[len(flag) // 2:])}")
- exploit.py → 밥과의 통신에서 앨리스로 위장하여 악의적인 값 전달
- g^x_a, g^x_b를 알 수 있으므로 (앨리스 비밀키 x_a, 밥 비밀키 x_b)
- g와 연관된 값을 송신하여 통신 종료
- 0(또는 1)을 송신하여 밥과 앨리스의 키를 0(또는 1)로 설정하게 함
from pwn import * from Crypto.Util.Padding import pad, unpad from Crypto.Cipher import AES import hashlib class Person(object): def __init__(self, p): self.p = p self.g = 2 self.x = random.randint(2, self.p - 1) def calc_key(self): self.k = pow(self.g, self.x, self.p) return self.k def set_shared_key(self, k): self.sk = pow(k, self.x, self.p) aes_key = hashlib.md5(str(self.sk).encode()).digest() self.cipher = AES.new(aes_key, AES.MODE_ECB) def encrypt(self, pt): return self.cipher.encrypt(pad(pt, 16)).hex() def decrypt(self, ct): return unpad(self.cipher.decrypt(bytes.fromhex(ct)), 16) io = remote("host3.dreamhack.games", 21537) io.recvuntil(b"Prime: ") p = int(io.recvline(), 16) not_alice, not_bob = Person(p), Person(p) # Communication 1 io.recvuntil(b"Key: ") not_bob.set_shared_key(int(io.recvline(), 16)) io.sendlineafter(">> ", str(not_alice.calc_key()).encode()) # Communication 2 io.recvuntil(b"Key: ") not_alice.set_shared_key(int(io.recvline(), 16)) io.sendlineafter(">> ", str(not_bob.calc_key()).encode()) # Alice's shared key is same with not_bob's shared key io.recvuntil(b"Alice: ") flag1 = not_bob.decrypt(io.recvline().decode()) # Bob's shared key is same with not_alice's shared key io.recvuntil(b"Bob: ") flag2 = not_alice.decrypt(io.recvline().decode()) io.close() print((flag1 + flag2).decode())
- g^x_a, g^x_b를 알 수 있으므로 (앨리스 비밀키 x_a, 밥 비밀키 x_b)
중간자 공격 실습 실습 - 이산 로그 문제
→ smooth한 수 만들기
- chall.py → 아래 두 가지 조건을 만족하는 512비트 소수 p를 찾아야함
- p-1 소인수분해 시 작은 소인수만으로 구성 (B-smooth)
- 100회 연속 이산 로그 문제 해결 가능
from Crypto.Util.number import isPrime import secrets flag = open("flag.txt", "r").read() pbits = 512 p = int(input("Prime: ")) g = int(input("g: ")) assert isPrime(p) and p.bit_length() == pbits for rnd in range(100): print(f"--- Round {rnd + 1} ---") secret = secrets.randbits(pbits) print(f"{pow(g, secret, p) = }") assert secret == int(input("secret: ")) print("Wow, you are the master of DLP!") print(flag)
- exploit.py
- g의 Multiplicative order → 페르마의 소정리에 의해 p와 서로소인 모든 정수 g에 대해 g^(p−1) ≡ 1 (mod p)를 만족 → 정수 g(1 ≤ g ≤ p−1)에 대하여 p−1번 곱하면 1이 나옴 (곱셈의 항등원)
- 원시근 → 3처럼 Multiplicative order가 p−1인 수
- 원시근 r과 모든 [1, p−1)에 속하는 i에 대해 다음이 성립 → r^i ≢ 1 (mod p)
- 원시근 → 3처럼 Multiplicative order가 p−1인 수
- 풀이
- p < 2^512를 만족하는 최댓값 k를 찾은 후, p = k * base + 1로 설정
- p를 소수로 만들기 위해 base를 하나씩 뺌
- g의 Multiplicative order → 페르마의 소정리에 의해 p와 서로소인 모든 정수 g에 대해 g^(p−1) ≡ 1 (mod p)를 만족 → 정수 g(1 ≤ g ≤ p−1)에 대하여 p−1번 곱하면 1이 나옴 (곱셈의 항등원)
from sage.all import * # 파이썬 코드로 바꾸기 위해 sage의 함수를 import from pwn import * from Crypto.Util.number import isPrime from tqdm import trange # io = process(["python3", "chall.py"]) io = remote("host3.dreamhack.games", "20106") pbits = 512 base = 1 for small_p in Primes(): base *= small_p if int(base).bit_length() >= pbits - 20: # base를 int형으로 바꿈 break k = 2**pbits // base # 연산자 수정 (^ → **) p = k * base + 1 while not isPrime(int(p)): # p를 int형으로 바꿈 p -= base Fp = GF(p) g = Fp.multiplicative_generator() io.sendlineafter(b": ", str(p).encode()) io.sendlineafter(b": ", str(g).encode()) for _ in trange(100): io.recvuntil(b"= ") res = int(io.recvline()) secret = Fp(res).log(g) io.sendlineafter(b": ", str(secret).encode()) io.interactive()
이산 로그 문제 실습 '24-여름 SISS > 암호학' 카테고리의 다른 글
[SISS/암호학 스터디] 여름 9주차 스터디 - 전자 서명 (0) 2024.08.24 [SISS/암호학 스터디] 여름 8주차 스터디 - 해시 (0) 2024.08.19 [SISS/암호학 스터디] 여름 6주차 스터디 - RSA (0) 2024.08.08