print(f"encrypted flag > {tdes.encrypt(flag, 'EDE').hex()}")
두 가지를 할 수 있다.
옵션 1: 내가 고른 메시지를 내가 고른 3글자 모드(E/D 조합)로 암호화. 단 DED은 금지.
옵션 2: 플래그를 EDE 모드로 암호화한 결과를 받음.
cipher.py의 TDES가 본체다.
class TDES: def __init__(self, key1, key2): self.keys = [key1, key2, key1] # ← K1, K2, K1 self.cipher = [DES.new(key, DES.MODE_ECB) for key in self.keys] def encrypt(self, plaintext, mode): # mode: 3글자, 각 'E'/'D' for cipher, m in zip(self.cipher, mode): if m == 'E': plaintext = cipher.encrypt(plaintext) # 자리 0,2 → K1 / 자리 1 → K2 else: plaintext = cipher.decrypt(plaintext) return plaintext
핵심은 두 가지다.
첫째, 키 배열이 [K1, K2, K1]이다. 즉 자리 0과 2는 항상 K1, 자리 1은 항상 K2로 처리된다.
둘째, mode의 각 글자가 그 자리에서 암호화(E)냐 복호화(D)냐를 정한다. encrypt(msg, mode)는 자리 순서대로 mode[0](K1) → mode[1](K2) → mode[2](K1)를 적용한다.
플래그는 EDE로 암호화되니, 우리가 받는 건 E_k1( D_k2( E_k1(flag) ) )다.
🧱 배경 — 3DES와 EDE
Triple-DES는 DES를 세 번 겹쳐 키 길이를 늘린 방식이다. 가장 흔한 게 EDE(Encrypt-Decrypt-Encrypt): C = E_k1( D_k2( E_k1(P) )). 가운데를 복호로 둔 건, 세 키를 다 같게(K1=K2) 두면 그냥 단일 DES가 되어 옛 장비와 호환되게 하려는 설계다.
Triple-DES — DES 블록을 세 번 연쇄(K1→K2→K3)해 M을 C로
출처: Wikimedia Commons, CC BY-SA 3.0
이 문제는 2키 EDE(K1, K2, K1)다. 복호는 같은 키로 거꾸로 — P = D_k1( E_k2( D_k1(C) )), 즉 DED다. 그런데 서버가 정확히 그 DED만 막아 놨다. 직접 복호는 못 한다는 뜻이다.
💣 핵심 — 연산자 대수로 DED를 재조립
암호화·복호화를 연산자로 두면 깔끔하다. A = E_k1, B = E_k2라 하면 A⁻¹ = D_k1, B⁻¹ = D_k2다. encrypt(x, mode)는 자리 0(K1)→1(K2)→2(K1) 순으로 적용하니, 함수로 쓰면 이렇다(왼쪽이 마지막 적용).
서버는 복호에 딱 필요한 DED 한 모드만 차단했다. 하지만 A와 B를 자리별로 독립적으로 켜고 끌 수 있는 한, DED와 똑같은 단어 A⁻¹·B·A⁻¹는 다른 모드들의 합성으로 얼마든지 만들어진다. 블랙리스트가 "값" 하나만 막고 그 값을 만드는 "경로"를 안 막으면 의미가 없다.
Textbook(교과서) 모드의 위험.
여기서 약점은 3DES 알고리즘 자체가 아니라, ECB로 블록을 독립 처리하고 자리별 E/D를 사용자에게 그대로 노출한 운용 방식이다. 암호 프리미티브를 날것으로 노출하면, 키를 몰라도 프리미티브의 대수 구조만으로 뚫린다. 실제 시스템이라면 모드를 사용자가 못 고르게 고정하고, 인증 암호화(AEAD)처럼 복호 자체를 통제하는 설계가 필요하다.
Comments
댓글
댓글을 남기려면 로그인이 필요해요. (네이버 · 구글 계정)
댓글 불러오는 중…