고전 암호는 컴퓨터와 같은 고성능 연산 장치가 발명되기 전, 비교적 간단한 기계와 손 등으로 암복호화를 수행하던 암호를 말한다. 고전 암호는 일반적으로 치환(Substitution)과 전치(Transposition)의 방법으로 설계된다.
단일 문자 치환 암호
대표적인 예:카이사르 암호로, 카이사르 암호는 평문의 각 알파벳을 정해진 횟수만큼 다음 순서에 해당하는 알파벳으로 치환한다.
사람 한 명이 한 글자에 대응되지만 규칙성이 없어서 암호문으로부터 평문을 유추할 수 없는 춤추는 인형 암호가 있다.
코드북을 이용한 단일 치환 암호는 현재도 종종 사용된다. 송신자와 수신자가 책을 정하고, 송신자가 책의 페이지 와 단어의 인덱스 를 보내면, 수신자는 책 페이지의 번째 단어를 확인하여 송신자의 메세지를 해독합니다. 이 암호 체계는 공작원에게 지령을 전달하는 목적으로 최근에도 쓰이고 있습니다.
다중 문자 치환 암호
다중 문자 치환 암호(Polyalphabetic substitution cipher)는 단일 문자 치환 암호와 달리, 평문의 한 문자가 암호문에서 여러 종류의 문자로 치환될수 있습니다
예를 들면 "SKY"로 "DREAMHAK"을 만들 수 있습니다.
전치 암호
전치 암호(Transposition cipher)는 평문을 구성하는 문자들의 순서를 재배열하여 암호문을 만듭니다. 평문을 정해진 길이의 블록들로 나누고, 규칙을 적용하여 블록 안의 문자들을 재배치합니다.
고전 암호 공격
전수 키 탐색 공격(Exhaustive key search attack)은 평문과 암호문을 알 때, 키 공간을 전부 탐색하며 주어진 암호문과 같은 암호문을 생성하는 키를 찾는 방법입니다.
빈도수 공격
단일 치환 암호는 평문의 문자와 암호문의 문자가 항상 일대일 대응을 이루기 때문에 평문의 통계적 특성이 유지된다.
현대 암호
학자들은 외부인이 키가 공유되는 과정을 도청해도, 공유되는 키는 알지 못하게 하는 키 공유 알고리즘(Key exchange algorithm)을 연구했다.
케르크호프스의 원리는 오귀스트 케르크호프스(Auguste Kerckhoffs)가 집필한 군사용 암호의 원칙 중에서 2번째 항목으로, 현대 암호 체계에서 중요한 의미를 가지고 있습니다.
혼돈과 확산
혼돈은 암호문에서 평문의 특성을 알아내기 힘든 성질이다.
확산은 평문의 작은 변화가 암호문의 큰 변화로 이어지는 성질이다.
대칭키 암호 시스템
블록 암호(Block cipher)는 평문을 정해진 크기의 블록 단위로 암호화하는 암호입니다.
블록 암호의 대표적인 예시로는 DES 대칭키 암호와 AES 대칭키 암호가 있다.
스트림 암호(Stream cipher)는 송신자와 수신자가 공유하는 데이터 스트림을 생성하고 이를 평문과 특정한 연산을 수행하여 암호문을 생성하는 암호입니다. 복호화 과정은 암호화 과정의 연산을 역으로 수행하여 진행됩니다.
암호의 기능
암호의 기능에는 기밀성, 무결성, 인증, 부인 방지가 있다.
실습
#!/usr/bin/env python3
from Crypto.Cipher import DES
import signal
import os
if __name__ == "__main__":
signal.alarm(15)
with open("flag", "rb") as f:
flag = f.read()
key = b'Dream_' + os.urandom(4) + b'Hacker'
key1 = key[:8]
key2 = key[8:]
print("4-byte Brute-forcing is easy. But can you do it in 15 seconds?")
cipher1 = DES.new(key1, DES.MODE_ECB)
cipher2 = DES.new(key2, DES.MODE_ECB)
encrypt = lambda x: cipher2.encrypt(cipher1.encrypt(x))
decrypt = lambda x: cipher1.decrypt(cipher2.decrypt(x))
print(f"Hint for you :> {encrypt(b'DreamHack_blocks').hex()}")
msg = bytes.fromhex(input("Send your encrypted message(hex) > "))
if decrypt(msg) == b'give_me_the_flag':
print(flag)
else:
print("Nope!")
확인할 부분
- signal.alarm(15)을 이용해 15초의 제한 시간이 주어져 있다.
- os.urandom 함수는 /dev/urandom이 만들어내는 난수로, CSPRNG(Cryptographically Secure Pseudorandom Number Generator)이다.
-DES.new(key1, DES.MODE_ECB) 와 같은 함수를 이용하여 cipher1 암호를 생성합니다. DES 는 대칭키 암호이기 때문에 키를 알면 암호화와 복호화가 모두 가능하고, 키를 모르면 둘 중 어느 것도 불가능하다는 특성을 가지고 있다.
솔브코드
from pwn import *
from Crypto.Cipher import DES
io = process(["python3", "prob.py"])
# io = remote("host3.dreamhack.games", 18664)
io.recvuntil(b":> ")
hint = bytes.fromhex(io.recvline().decode())
conflict = dict()
for i in range(65536):
b = i.to_bytes(2, "big")
cipher = DES.new(b"Dream_" + b, DES.MODE_ECB)
enc = cipher.encrypt(b"DreamHack_blocks")
conflict[enc] = b"Dream_" + b
for i in range(65536):
b = i.to_bytes(2, "big")
cipher = DES.new(b + b"Hacker", DES.MODE_ECB)
dec = cipher.decrypt(hint)
if dec in conflict:
key1 = conflict[dec]
key2 = b + b"Hacker"
break
cipher1 = DES.new(key1, DES.MODE_ECB)
cipher2 = DES.new(key2, DES.MODE_ECB)
encrypt = lambda x: cipher2.encrypt(cipher1.encrypt(x))
assert encrypt(b"DreamHack_blocks") == hint
io.sendlineafter(b'> ', encrypt(b"give_me_the_flag").hex().encode())
flag = eval(io.recvline())
io.close()
print(flag.decode())