๋ฌธ์ : DreamHack โ STREME REVERIE ๋ถ๋ฅ: crypto ๋์ด๋: ๐ฅ Bronze 2 FLAG:
DH{855083595b1557aeb8f60e7422e49405672b8ddf793700d884dae710909f9d0f}
๋ฌธ์ ์ค๋ช ์ ๋ ์ค์ด ์ ๋ถ๋ค. "Stream Cipher, to the Extreme!", ํ๊ตญ์ด๋ก๋ "์คํธ๋ฆผ ์ํธ๋ฅผ ์ต์คํธ๋ฆผํ๊ฒ ๋ฐ๊ฟ๋ดค์ต๋๋ค!".
์ต์คํธ๋ฆผํ๊ฒ ๋ฐ๊ฟจ๋ค๋ ๊ฒ ๋ญ๊ฐ ์ถ์ด ํ์ผ์ ์ด์ด ๋ณด๋ฉด, ํ๋ฒํ 32๋นํธ LFSR ํ๋๊ฐ ๋ฐ์ดํธ๋ง๋ค 8๋นํธ์ฉ ํค์คํธ๋ฆผ์ ๋ฝ์ XOR ํ๋ ๊ตฌ์กฐ๋ค. ์ต์คํธ๋ฆผํ ๊ฑด ์ํธ๊ฐ ์๋๋ผ ํญ ์์น(32, 22, 2, 1)์ ์๊น์ ์ชฝ์ ๊ฐ๊น๋ค.
๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | STREME REVERIE (DreamHack #1923) |
| ๋์ด๋ | ๐ฅ Bronze 2 |
| ๋ถ๋ฅ | crypto |
| ์ ๊ณต ํ์ผ | cipher.py ยท prob.py ยท output.txt ยท flag(๋๋ฏธ) |
| ์๋ฒ | ์์ โ ์คํ๋ผ์ธ ๋ฌธ์ |
| ํต์ฌ ์ทจ์ฝ์ | LFSR ์ถ๋ ฅ ๋นํธ๊ฐ ๊ทธ๋๋ก ๋ด๋ถ ์ํ๋ก ๋๋จน์ โ ํค์คํธ๋ฆผ 32๋นํธ = ์ํ ์ ์ฒด |
ํ์ด ํ๋ฆ์ ์งง๋ค. ํ๋๊ทธ๊ฐ DH{ ๋ก ์์ํ๋ค๋ ์ฌ์ค์์ ํค์คํธ๋ฆผ 24๋นํธ๋ฅผ ์ป๊ณ , ๋ชจ์๋ 8๋นํธ๋ฅผ 256๊ฐ์ง๋ก ํ์ด 32๋นํธ๋ฅผ ์ฑ์ด๋ค. ๊ทธ 32๋นํธ๊ฐ ๊ณง ์ด๋ ์์ ์ ๋ด๋ถ ์ํ์ด๋ฏ๋ก, ์ํํธ๋ฅผ 32๋ฒ ๊ฑฐ๊พธ๋ก ๋๋ฆฌ๋ฉด ์๋๊ฐ ๋์จ๋ค.
๐งฉ ๋ฐฐ๊ฒฝ โ LFSR ์ ์ด๋ป๊ฒ ๋๋๊ฐ
LFSR(Linear Feedback Shift Register)์ ๋นํธ ๋ ์ง์คํฐ๋ฅผ ํ ์นธ์ฉ ๋ฐ๋ฉด์, ์ง์ ๋ ๋ช ์๋ฆฌ(ํญ)๋ฅผ XOR ํ ๊ฐ์ ๋น ์๋ฆฌ์ ์ฑ์ ๋ฃ๋ ๊ตฌ์กฐ๋ค. ์๋๋ ์ํค๋ฏธ๋์ด์ ์๋ 16๋นํธ ํผ๋ณด๋์น LFSR ๊ทธ๋ฆผ์ด๋ค. ํญ์ด 11ยท13ยท14ยท16๋ฒ ์๋ฆฌ์ ๊ฑธ๋ ค ์๊ณ , ๊ทธ XOR ๊ฒฐ๊ณผ๊ฐ ๋งจ ์์ผ๋ก ๋ค์ด๊ฐ๋ค.

์ถ์ฒ: Wikimedia Commons, KCAuXy4p, CC0
ํญ ์์น๋ฅผ ๋คํญ์์ผ๋ก ์ ๋ ๊ด๋ก๊ฐ ์๋ค. ์ด ๋ฌธ์ ์ฝ๋ ์ฃผ์์ ์ ํ x^32 + x^22 + x^2 + x + 1 ์ด ๊ทธ๊ฒ์ด๋ค. ์ด ๋คํญ์์ด GF(2) ์์์ ์์(primitive)๋ฉด ๋ ์ง์คํฐ๋ 0์ด ์๋ ๋ชจ๋ ์ํ๋ฅผ ํ ๋ฒ์ฉ ํ๊ณ ๋์์จ๋ค. ์ฃผ๊ธฐ๊ฐ 2^32 โ 1 ์ด๋ผ๋ ๋ป์ด๊ณ , ์ค์ ๋ก ๊ทธ๋ ๋ค๋ ๊ฑด ๋ค์์ SageMath ๋ก ํ์ธํ๋ค.
์ฃผ๊ธฐ๊ฐ ๊ธธ๋ค๋ ๊ฑด ํต๊ณ์ ์ผ๋ก ๋๋คํด ๋ณด์ธ๋ค๋ ๋ป์ผ ๋ฟ, ์์ธก ๋ถ๊ฐ๋ฅํ๋ค๋ ๋ป์ด ์๋๋ค. LFSR ์ ์ด๋ฆ ๊ทธ๋๋ก ์ ํ์ด๋ผ ์ถ๋ ฅ ๋นํธ๋ค์ด ์ ๋ถ ์ด๊ธฐ ์ํ์ XOR ์กฐํฉ์ด๋ค. ๋ช ๋นํธ๋ง ์๋ฉด ๋๋จธ์ง๊ฐ ๊ฒฐ์ ๋๋ค. ์คํธ๋ฆผ ์ํธ์ ํค์คํธ๋ฆผ ์์ฑ๊ธฐ๋ก LFSR ์ ๋ ๊ฒ ๊ทธ๋๋ก ์ฐ๋ฉด ์ ๋๋ ์ด์ ๊ฐ ์ฌ๊ธฐ ์๋ค.
๐ฌ ์ ์ฐฐ โ ๋ฐฐํฌ๋ณธ ์ธ ํ์ผ
๋ฐ์ zip ์ ํ๋ฉด ํ์ผ์ด ๋ท์ด๋ค. flag ๋ ๋ก์ปฌ ํ
์คํธ์ฉ ๋๋ฏธ(DH{FAKE_FLAG})๋ผ ๋ณผ ๊ฒ ์๊ณ , ์ง์ง๋ output.txt ํ ์ค์ด๋ค.
ls -l extracted && cat extracted/output.txt
hex 136์๋ฆฌ, ์ฆ ์ํธ๋ฌธ์ 68๋ฐ์ดํธ๋ค. ํ๋๊ทธ ํ์์ด DH{ + 64๊ธ์ + } ๋ผ๋ฉด ๋ฑ ๋ค์ด๋ง๋๋ค. ์์ชฝ 64๊ธ์๋ ์๋ง sha256 hex ์ผ ๊ฒ์ด๋ค.
prob.py ๋ ์งง๋ค.
#!/usr/bin/env python3
from cipher import STREAM
import random
if __name__ == "__main__":
with open("flag", "rb") as f:
flag = f.read()
assert flag[:3] == b'DH{' and flag[-1:] == b'}'
seed = random.getrandbits(32)
stream = STREAM(seed, 32)
print(f"encrypted flag > {stream.encrypt(flag).hex()}")์ฌ๊ธฐ์ ๋ ๊ฐ์ง๊ฐ ํ์ ๋๋ค. ์๋๋ 32๋นํธ๊ณ , ํ๋๊ทธ๋ DH{ ๋ก ์์ํด } ๋ก ๋๋๋ค. assert ๊ฐ ๋๋๊ณ ์๋ ค์ฃผ๋ ์
์ด๋ผ ์๋ ค์ง ํ๋ฌธ 3๋ฐ์ดํธ๋ฅผ ๊ณต์ง๋ก ๋ฐ๋๋ค.
์๋๊ฐ random.getrandbits(32) ์์ ๋์ค๋ ๊ฒ๋ ์ ๊น ๋๊ธธ์ด ๊ฐ๋ค. ๋ฉ๋ฅด์ผ ํธ์์คํฐ๋ผ ์ถ๋ ฅ 624๊ฐ๋ฅผ ๋ชจ์ผ๋ฉด ๋ด๋ถ ์ํ๋ฅผ ๋ณต์ํ ์ ์๋ ๊ฑธ๋ก ์ ๋ช
ํ๋ฐ, ์ฌ๊ธฐ์๋ ๋ฑ ํ ๋ฒ ๋ฝ๊ณ ๋์ด๋ค. ๊ฐ ํ๋๋ก๋ ์๋ฌด๊ฒ๋ ๋ชป ํ๋ค. ๊ทธ์ชฝ ๊ธธ์ ๋งํ ์๋ค.
์ํธ ๋ณธ์ฒด๋ cipher.py ๋ค.
class STREAM:
def __init__(self, seed, size):
self.state = self.num2bits(seed, size)
# x^32 + x^22 + x^2 + x^1 + 1
self.taps = (32, 22, 2, 1)
def num2bits(self, num, size):
assert num < (1 << size)
return bin(num)[2:].zfill(size)
def bits2num(self, bits):
return int('0b' + bits, 2)
def shift(self):
new_bit = 0
for tap in self.taps:
new_bit ^= int(self.state[tap - 1])
new_bit = str(new_bit)
self.state = new_bit + self.state[:-1]
return new_bit
def getNbits(self, num):
sequence = ""
for _ in range(num):
sequence += self.shift()
return sequence
def encrypt(self, plaintext):
ciphertext = b""
for p in plaintext:
stream = self.bits2num(self.getNbits(8))
c = p ^ stream
ciphertext += bytes([c])
return ciphertext์ํ๋ฅผ ๋นํธ ๋ฌธ์์ด๋ก ๋ค๊ณ ๋ค๋๋ ๊ตฌํ์ด๋ค. shift() ๋ฅผ ํ ์ค์ฉ ๋ณด๋ฉด ์ด๋ ๋ค.
- ํญ ์๋ฆฌ
state[31],state[21],state[1],state[0]์ XOR ํด์new_bit์ ๋ง๋ ๋ค. state = new_bit + state[:-1]โ ์ ๋นํธ๋ฅผ ๋งจ ์์ ๋ถ์ด๊ณ ๋งจ ๋ค ํ ์นธ์ ๋ฒ๋ฆฐ๋ค.- ๊ทธ๋ฆฌ๊ณ ๊ทธ
new_bit์ ๊ทธ๋๋ก ๋ฐํํ๋ค. ์ด๊ฒ ํค์คํธ๋ฆผ ๋นํธ๋ค.
๋ฐ์ดํธ ํ๋๋ฅผ ์ํธํํ ๋๋ง๋ค getNbits(8) ๋ก 8๋นํธ๋ฅผ ๋ฝ์ ์ ์๋ก ๋ฐ๊พผ ๋ค XOR ํ๋ค. ๋จผ์ ๋์จ ๋นํธ๊ฐ ์์ ๋นํธ๋ค.
โถ๐ ์ฝ์ง โ ์ํธ๋ฌธ์ 68๋ฐ์ดํธ๊ฐ ์๋๋ผ 1๋ฐ์ดํธ๋ก ์ฝ์๋ค
output.txt ์์ hex ๋ง ์๋ผ๋ด๋ ค๊ณ ์ ๊ท์์ ๋์ถฉ ์ผ๋ค๊ฐ ์๊ฐ์ ๋ฒ๋ ธ๋ค. ํ์ผ ๋ด์ฉ์ด encrypted flag > c615... ๋ผ์ ์ฒซ ๋งค์น๋ฅผ ๊ทธ๋๋ก ์ฐ๋ฉด ์์ชฝ ์์ด ๋จ์ด์ ๋ฐํ ์กฐ๊ฐ์ด ์กํ๋ค. encrypted ์์ ed ๊ฐ ๊ทธ๊ฒ์ด๋ค. ๋ ๋ค [0-9a-f] ๋ก๋ง ์ด๋ฃจ์ด์ ธ ์์ผ๋ ์ ๊ท์์ ์๋ฌด ์๋ชป์ด ์๋ค.
#!/usr/bin/env python3
"""์ฝ์ง ์ฌํ โ output.txt ๋ฅผ ์ ๊ท์์ผ๋ก ์๋ผ๋ด๋ค 1๋ฐ์ดํธ๋ง ์ง์ ์ฌ๊ฑด.
`encrypted flag > c615...` ์ ์์ชฝ ์์ด ๋จ์ด๋ [0-9a-f] ๋ก๋ง ์ด๋ฃจ์ด์ง ์กฐ๊ฐ์
ํ๊ณ ์์ด์, ์ฒซ ๋งค์น๋ฅผ ๊ทธ๋๋ก ์ฐ๋ฉด ์ํธ๋ฌธ์ด ์๋๋ผ ๊ทธ ์กฐ๊ฐ์ด ์กํ๋ค.
"""
import re
txt = open("extracted/output.txt").read()
first = re.search(r"([0-9a-f]{2,})", txt).group(1)
longest = max(re.findall(r"[0-9a-f]{2,}", txt), key=len)
print(f"[์ฒซ ๋งค์น ] {first!r} โ {len(bytes.fromhex(first))}๋ฐ์ดํธ")
print(f"[๊ฐ์ฅ ๊ธด ] {longest[:24]}... โ {len(bytes.fromhex(longest))}๋ฐ์ดํธ")
print()
print("[*] 'encrypted' ์์ ์กฐ๊ฐ:", [m for m in re.findall(r"[0-9a-f]{2,}", txt)][:3])python3 sapjil_regex.py
์ฆ์์ด ๊ณ ์ฝํ๋ ๊ฑด ์๋ฌ๊ฐ ์ ๋ฌ๋ค๋ ์ ์ด๋ค. 1๋ฐ์ดํธ ์ํธ๋ฌธ์ผ๋ก๋ ํค์คํธ๋ฆผ 8๋นํธ๋ ๋ฉ์ฉกํ ๋ฝํ๊ณ , ๊ทธ๋ค์ ์ํ ๋ณต์ ๋จ๊ณ์์ ๋ฌธ์์ด ์ธ๋ฑ์ค๊ฐ ๋ฒ์๋ฅผ ๋ฒ์ด๋ ์ฃฝ๋๋ค. ์๋ฑํ ๊ณณ์์ ํฐ์ง๋ ์์ธ์ ์์ชฝ์์ ์ฐพ๊ฒ ๋๋ค. ๊ฒฐ๊ตญ ๊ฐ์ฅ ๊ธด ๋งค์น๋ฅผ ๊ณ ๋ฅด๋ ๊ฑธ๋ก ๋ฐ๊ฟจ๋ค.
๐ฃ ํต์ฌ โ ์ถ๋ ฅ ๋นํธ๊ฐ ๊ณง ์ํ๋ค
state = new_bit + state[:-1] ์ด ํ ์ค์ด ์ด ๋ฌธ์ ์ ์ ๋ถ๋ค.
์๋ก ๋ง๋ ๋นํธ๋ฅผ ์์ ๋ฐ์ด ๋ฃ๊ณ , ๊ทธ ๋นํธ๋ฅผ ๊ทธ๋๋ก ๋ฐ์ผ๋ก ๋ด๋ณด๋ธ๋ค. ๊ทธ๋ฌ๋ 32์คํ ์ ๋๋ฆฌ๋ฉด ์๋ ์๋ 32๋นํธ๋ ์ ๋ถ ์ค๋ฅธ์ชฝ์ผ๋ก ๋ฐ๋ ค ์ฌ๋ผ์ง๊ณ , ๋ ์ง์คํฐ ์์๋ ๋ฐฉ๊ธ ๋ฐ์ผ๋ก ๋ด๋ณด๋ธ ํค์คํธ๋ฆผ 32๋นํธ๋ง ๋จ๋๋ค. ์์๋ง ๋ค์งํ ์์ ๋ฟ์ด๋ค.

์ฆ ํค์คํธ๋ฆผ 32๋นํธ๋ฅผ ๊ด์ธกํ๋ฉด ๊ทธ ์์ ์ ๋ด๋ถ ์ํ๋ฅผ ์๋ค. ์ด๊ฑด ์ ๋๊ฐ ์๋๋ผ ์ ์์ ๊ฐ๊น๋ค. ๊ทธ๋๋ ์ฝ๋๋ก ํ ๋ฒ ํ์ธํ๊ณ ๋์ด๊ฐ๋ ๊ฒ ๋ซ๋ค. ์๋๋ฅผ ์๋ฌด๊ฑฐ๋ ํ๋ ๋ฃ๊ณ 32์คํ ์ ๋๋ฆฐ ๋ค, ์ํ์ ํค์คํธ๋ฆผ์ ๋๋ํ ์ฐ์ด ๋ดค๋ค.
#!/usr/bin/env python3
"""LFSR ์ '์ถ๋ ฅ ๋นํธ = ๋ค์ ์ํ' ๊ด๊ณ๋ฅผ ๋์ผ๋ก ํ์ธํ๋ค.
32์คํ
์ ๋๋ฆฐ ๋ค์ ๋ด๋ถ ์ํ๊ฐ ๊ทธ๋์ ๋ฑ์ ํค์คํธ๋ฆผ 32๋นํธ๋ฅผ ๋ค์ง์ ๊ฒ๊ณผ
๊ฐ์์ง ์ง์ ๋น๊ตํ๋ค. ๊ฐ๋ค๋ฉด ํค์คํธ๋ฆผ 32๋นํธ๋ง ์์๋ ์ํ๊ฐ ๋ณต์๋๋ค๋ ๋ป์ด๋ค.
"""
import sys
sys.path.insert(0, "extracted")
from cipher import STREAM
SEED = 0xFE5DB0A9
s = STREAM(SEED, 32)
print(f"[*] seed = 0x{SEED:08x}")
print(f"[*] state = {s.state} (state[0] ์ด ์ต์ ๋นํธ ์๋ฆฌ)")
print()
ks = ""
for step in range(1, 9):
bit = s.shift()
ks += bit
print(f" step {step:2d}: ์ถ๋ ฅ={bit} state={s.state}")
print()
ks += s.getNbits(24) # ํฉ์ณ์ 32๋นํธ
print(f"[*] 32์คํ
ํค์คํธ๋ฆผ : {ks}")
print(f"[*] 32์คํ
๋ค ๋ด๋ถ ์ํ : {s.state}")
print(f"[*] ํค์คํธ๋ฆผ์ ๋ค์ง์ ๊ฒ : {ks[::-1]}")
print(f"[+] ์ํ == ๋ค์ง์ ํค์คํธ๋ฆผ ? {s.state == ks[::-1]}")python3 trace_lfsr.py
์คํ
1์์ ์ถ๋ ฅ 1 ์ด ์ํ ๋งจ ์์ ๋ถ๊ณ , ์๋ ์ํ๊ฐ ํ ์นธ์ฉ ์ค๋ฅธ์ชฝ์ผ๋ก ๋ฐ๋ฆฌ๋ ๊ฒ ๊ทธ๋๋ก ๋ณด์ธ๋ค. ๋ง์ง๋ง ์ธ ์ค์ด ๊ฒฐ๋ก ์ด๋ค. 32์คํ
๋ค ์ํ์ ํค์คํธ๋ฆผ 32๋นํธ์ ์ญ์์ด ํ ๊ธ์๋ ๋ค๋ฅด์ง ์๋ค.
์ด์ ํ์ํ ๊ฑด ์ฐ์๋ ํค์คํธ๋ฆผ 32๋นํธ๋ฟ์ด๋ค. ์ฐ๋ฆฌ๊ฐ ์๋ ๊ฑด 24๋นํธ๋ค.
โถ๐ ์ฝ์ง โ ์๋ 2^32 ๊ฐ๋ฅผ ํต์งธ๋ก ํ์ ๋ปํ๋ค
๊ตฌ์กฐ๋ฅผ ๋ณด๊ธฐ ์ ์ ๋จผ์ ๋ ์๊ฐ์ "32๋นํธ๋ฉด ๊ทธ๋ฅ ๋ค ๋๋ ค๋ ๋๋ ๊ฑฐ ์๋๊ฐ" ์๋ค. ์ค์ ๋ก ์ผ๋ง๋ ๊ฑธ๋ฆฌ๋์ง ์ฌ ๋ดค๋ค. ํ๋ณด ํ๋๋น 3๋ฐ์ดํธ๋ง ๋ณตํธํด DH{ ์ธ์ง ํ์ธํ๋, ๊ฐ๋ฅํ ํ ์ผ ํ์ ์ผ๋ก ์ก์๋ค.
#!/usr/bin/env python3
"""์๋ 32๋นํธ๋ฅผ ํต์งธ๋ก ํ๋ ๋ฌด์ํ ๋ฐฉ๋ฒ์ด ์ค์ ๋ก ์ผ๋ง๋ ๊ฑธ๋ฆฌ๋์ง ์ฌ ๋ณธ๋ค.
ํ ํ๋ณด๋ฅผ ํ์ธํ๋ ค๋ฉด ์ต์ 3๋ฐ์ดํธ(=24์คํ
)๋ฅผ ๋ณตํธํด 'DH{' ์ธ์ง ๋ด์ผ ํ๋ค.
๊ทธ ๋น์ฉ์ 1000๊ฐ๋ก ์ค์ธกํ ๋ค 2^32 ๋ก ํ์ฐํ๋ค.
"""
import sys
import time
sys.path.insert(0, "extracted")
from cipher import STREAM
ct = bytes.fromhex(open("extracted/output.txt").read().split(">")[1].strip())
N = 1000
t0 = time.perf_counter()
for seed in range(N):
if STREAM(seed, 32).decrypt(ct[:3]) == b"DH{":
print(f" (์ฐ์ฐํ ๋ง์ ์๋: {seed})")
dt = time.perf_counter() - t0
per = dt / N
total = per * (1 << 32)
print(f"[*] ํ๋ณด {N}๊ฐ ํ์ธ์ {dt:.3f}์ด โ ๊ฐ๋น {per * 1e6:.1f}us")
print(f"[*] 2^32 = {1 << 32:,} ๊ฐ ์ ๋ถ๋ฉด {total:,.0f}์ด = {total / 86400:.1f}์ผ")
print(f"[*] ๋ฐ๋ฉด ์ฐ๋ฆฌ๊ฐ ์ค์ ๋ก ํ์ ํ๋ณด ์: 2^8 = 256 ๊ฐ ({per * 256:.4f}์ด)")python3 bench_bruteforce.py
๊ฐ๋น 16๋ง์ดํฌ๋ก์ด, ์ ๋ถ ํ์ผ๋ฉด ํ๋ฃจ๊ฐ ์ฑ ์ ๊ฑธ๋ฆฐ๋ค. ํ์ด์ฌ ์์ ๊ตฌํ์ผ๋ก ์ด ์ ๋๋ฉด C ๋ก ์ง์ ์ฝ์ด ๋ช ๊ฐ ๋ถ์ด๋ฉด ๋ช ๋ถ์ง๋ฆฌ๋ค. ์ฆ "๋ชป ํธ๋" ๊ฑด ์๋๋ค.
๊ทธ๋๋ ์ด ๊ธธ๋ก ๊ฐ๋ฉด ๋ฌธ์ ๋ฅผ ํผ ๊ฒ ์๋๋ผ ๊ธฐ๋ค๋ฆฐ ๊ฒ ๋๋ค. ๊ตฌ์กฐ๋ฅผ ๋ณด๋ฉด 256๋ฒ์ด๋ฉด ๋๋๋๋ฐ 43์ต ๋ฒ์ ๋ ์ด์ ๊ฐ ์๋ค. ๊ทธ๋์ ์ฌ๊ธฐ์ ๋ฉ์ถ๊ณ ์ฝ๋๋ฅผ ๋ค์ ์ฝ์๋ค.
๐ฏ ๋๊ฐ๊ธฐ โ unshift ๋ฅผ 32๋ฒ
ํค์คํธ๋ฆผ 32๋นํธ๋ฅผ ์์ ๋ฃ์๋ค๊ณ ์น์. ๊ทธ๊ฑด 32์คํ ๋ค์ ์ํ๋ค. ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฑด ์๋, ์ฆ 0์คํ ์์ ์ ์ํ๋ค. ์ํํธ๋ฅผ ๊ฑฐ๊พธ๋ก ๋๋ ค์ผ ํ๋ค.
ํ ์คํ ์ ๋๊ฐ๋ ๊ฑด ์ด๋ ต์ง ์๋ค. ์ ๋ฐฉํฅ์ด ์ด๋ฌ๋ค.
new = prev[31] ^ prev[21] ^ prev[1] ^ prev[0]
cur = new + prev[:-1]๋ ๋ฒ์งธ ์ค์์ cur[0] = new, ๊ทธ๋ฆฌ๊ณ cur[1..31] = prev[0..30] ์ด๋ค. ๊ทธ๋ฌ๋ ์ด์ ์ํ์ ์ 31์นธ์ ํ์ฌ ์ํ์ ๊ทธ๋๋ก ๋จ์ ์๋ค. ์ฌ๋ผ์ง ๊ฑด prev[31] ํ๋๋ฟ์ธ๋ฐ, ๊ทธ๊ฑด ์ฒซ ์ค์ ์ฎ๊ธฐ๋ฉด ๋์จ๋ค.
prev[31] = new ^ prev[21] ^ prev[1] ^ prev[0]
= cur[0] ^ cur[22] ^ cur[2] ^ cur[1]prev[21] ์ cur[22], prev[1] ์ cur[2], prev[0] ์ cur[1] ์ด๋ฏ๋ก ์ ๋ถ ํ์ฌ ์ํ์ ๊ฐ์ผ๋ก ๋ฐ๋๋ค. ์ ๋ณด๊ฐ ํ๋๋ ์ ๋ฒ๋ ค์ก์ผ๋ ๋๊ฐ๊ธฐ๊ฐ ์ ์ผํ๊ฒ ๊ฒฐ์ ๋๋ค.
def unshift(state):
prev31 = int(state[0]) ^ int(state[22]) ^ int(state[2]) ^ int(state[1])
return state[1:] + str(prev31)์ด๊ฑธ 32๋ฒ ๋ถ๋ฅด๋ฉด ์๋๋ค.
๋จ์ ๊ฑด ํค์คํธ๋ฆผ 32๋นํธ๋ฅผ ์ฑ์ฐ๋ ์ผ์ด๋ค. ํ๋ฌธ DH{ ์ ์ํธ๋ฌธ ์ 3๋ฐ์ดํธ๋ฅผ XOR ํ๋ฉด 24๋นํธ๊ฐ ๋์จ๋ค. ๋ชจ์๋ 8๋นํธ๋ ๋ค ๋ฒ์งธ ํ๋ฌธ ๊ธ์์ ์ฐ์ธ ํค์คํธ๋ฆผ์ด๋ผ ์ ์ ์๋ค. ๊ทธ๋์ ๊ทธ ํ ๋ฐ์ดํธ๋ฅผ 256๊ฐ์ง๋ก ํ๋๋ค. ํ์ ๊ณต๊ฐ์ด 2^32 ์์ 2^8 ๋ก ์ค์๋ค.
ํ๋ณด๊ฐ ํ๋๋ก ์ขํ์ง๋์ง๋ ํ์ธํด ๋ ํ์๊ฐ ์๋ค. 256๊ฐ ์ ๋ถ๋ฅผ ๋ณตํธํด์ ์กฐ๊ฑด๋ณ๋ก ์ธ์ด ๋ดค๋ค.
#!/usr/bin/env python3
"""ํ๋ณด 256๊ฐ๊ฐ ๊ฐ๊ฐ ์ด๋ค ํ๋ฌธ์ ๋ฑ๋์ง, ์ด๋ค ์กฐ๊ฑด์์ ํ๋๋ก ์ขํ์ง๋์ง ์ผ๋ค.
์ 3๋ฐ์ดํธ๋ ์ด๋ค ํ๋ณด๋ฅผ ๋ฃ์ด๋ 'DH{' ๊ฐ ๋์จ๋ค โ ๊ทธ๊ฑธ๋ก ์ํ๋ฅผ ๋ง๋ค์์ผ๋ ๋น์ฐํ๋ค.
๊ทธ๋์ 4๋ฒ์งธ ๊ธ์๋ถํฐ๊ฐ ํ๋ณ ๊ทผ๊ฑฐ๋ค.
"""
import string
import sys
sys.path.insert(0, "extracted")
from cipher import STREAM
from solve import load_ct, unshift
ct = load_ct()
ks = "".join(f"{c ^ p:08b}" for c, p in zip(ct, b"DH{"))
printable = set(string.printable[:-5].encode())
ok_close, ok_all, hits = 0, 0, []
for guess in range(0x100):
state = (ks + f"{guess:08b}")[::-1]
for _ in range(32):
state = unshift(state)
pt = STREAM(int(state, 2), 32).decrypt(ct)
if guess < 3:
print(f" ํ๋ณด {guess:3d}: {pt[:40]!r}")
if pt[-1:] == b"}":
ok_close += 1
if set(pt) <= printable:
ok_all += 1
hits.append((guess, pt))
print()
print(f"[*] 'DH{{' ๋ก ์์ํ๋ ํ๋ณด : 256 / 256 (์ํ๋ฅผ ๊ทธ๋ ๊ฒ ๋ง๋ค์์ผ๋ ์ ๋ถ)")
print(f"[*] '}}' ๋ก ๋๋๋ ํ๋ณด : {ok_close} / 256")
print(f"[*] ์ ๋ถ ์ถ๋ ฅ๊ฐ๋ฅ ๋ฌธ์ : {ok_all} / 256")
for guess, pt in hits:
print(f"[+] ์ ์ผ ํ๋ณด (4๋ฒ์งธ ๊ธ์ ํค์คํธ๋ฆผ {guess:08b}): {pt.decode()}")python3 candidates.py
์ ์ธ ๊ธ์๋ ์ด๋ค ํ๋ณด๋ฅผ ๋ฃ์ด๋ DH{ ๊ฐ ๋์จ๋ค. ๊ทธ๋ ๊ฒ ๋๋๋ก ์ํ๋ฅผ ๋ง๋ค์์ผ๋ ํ๋ณ์ ๋ชป ์ด๋ค. ๋ค ๋ฒ์งธ ๊ธ์๋ถํฐ๊ฐ ๊ทผ๊ฑฐ๋ค. ๋ง์ง๋ง ๋ฐ์ดํธ๊ฐ } ์ธ ํ๋ณด๊ฐ 1๊ฐ, ์ ๋ถ ์ถ๋ ฅ ๊ฐ๋ฅํ ๋ฌธ์์ธ ํ๋ณด๋ 1๊ฐ. ๋์ด ๊ฐ์ ํ๋ณด๋ค.
๐ Full Exploit
์ ๋ฆฌํ๋ฉด ์ด๋ ๋ค. ์ํธ๋ฌธ์ ์ฝ๊ณ , ์๋ ค์ง ํ๋ฌธ์ผ๋ก 24๋นํธ๋ฅผ ๋ง๋ค๊ณ , 8๋นํธ๋ฅผ ๋ถ์ฌ 32๋นํธ๋ฅผ ์ฑ์ฐ๊ณ , ๋ค์ง์ด์ ์ํ๋ก ์ผ๊ณ , 32๋ฒ ๋๊ฐ์ ์๋๋ฅผ ์ป๊ณ , ๊ทธ ์๋๋ก ์ ์ฒด๋ฅผ ๋ณตํธํ๋ค. ํํฐ๋ ๋ ๊ฐ๋ฉด ์ถฉ๋ถํ๋ค.
#!/usr/bin/env python3
"""STREME REVERIE (DreamHack #1923, Bronze 2, crypto) ํ์ด.
32๋นํธ LFSR ์ ์ถ๋ ฅ ๋นํธ๊ฐ ๊ทธ๋๋ก ๋ค์ ์ํ๋ก ๋ค์ด๊ฐ๋ฏ๋ก,
์ฐ์๋ ํค์คํธ๋ฆผ 32๋นํธ๋ฅผ ์๋ฉด ๊ทธ ์์ ์ ๋ด๋ถ ์ํ๊ฐ ํต์งธ๋ก ๋ณต์๋๋ค.
์๋ ค์ง ํ๋ฌธ "DH{" ๋ 24๋นํธ๋ฟ์ด๋ผ ๋จ์ 8๋นํธ(=ํ๋๊ทธ 4๋ฒ์งธ ๊ธ์์ ์ฐ์ธ ํค์คํธ๋ฆผ)๋ง
256๊ฐ์ง๋ก ํ๊ณ , ์ํ๋ฅผ 32์คํ
๋๊ฐ์ ์๋๋ฅผ ์ป๋๋ค.
์ฌ์ฉ๋ฒ: python3 solve.py (๊ฐ์ ํด๋์ extracted/output.txt ๋ฅผ ์ฝ๋๋ค)
"""
import re
import string
from pathlib import Path
HERE = Path(__file__).resolve().parent
TAPS = (32, 22, 2, 1) # x^32 + x^22 + x^2 + x + 1
# โโ ๋ฌธ์ ์ STREAM ์ ๊ทธ๋๋ก ์ฎ๊ธด ๊ฒ (๊ฒ์ฆ์ฉ) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
class STREAM:
def __init__(self, seed, size):
self.state = bin(seed)[2:].zfill(size)
self.taps = TAPS
def shift(self):
new_bit = 0
for tap in self.taps:
new_bit ^= int(self.state[tap - 1])
new_bit = str(new_bit)
self.state = new_bit + self.state[:-1]
return new_bit
def getNbits(self, num):
return "".join(self.shift() for _ in range(num))
def decrypt(self, ciphertext):
out = b""
for c in ciphertext:
out += bytes([c ^ int(self.getNbits(8), 2)])
return out
# โโ LFSR ์ ํ ์คํ
๋๊ฐ๊ธฐ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def unshift(state):
"""state = new_bit + prev[:-1] ์ด๋ฏ๋ก prev[:31] ์ ๊ทธ๋๋ก ๋ณด์ด๊ณ ,
์ฌ๋ผ์ง prev[31] ์ ํญ ๊ด๊ณ์์์ ๋์ด๋ฆฐ๋ค."""
prev31 = int(state[0]) ^ int(state[22]) ^ int(state[2]) ^ int(state[1])
return state[1:] + str(prev31)
def load_ct():
txt = (HERE / "extracted" / "output.txt").read_text()
# "encrypted flag > ..." ์ ์๋ถ๋ถ๋ [0-9a-f] ๋ก ์ฝํ๋ฏ๋ก ๊ธธ์ด๋ก ๊ณจ๋ผ๋ธ๋ค
return bytes.fromhex(max(re.findall(r"[0-9a-f]{2,}", txt), key=len))
def main():
ct = load_ct()
known = b"DH{"
# ์๋ ค์ง ํ๋ฌธ 3๋ฐ์ดํธ โ ํค์คํธ๋ฆผ 24๋นํธ (๋จผ์ ๋์จ ๋นํธ๊ฐ MSB)
ks = "".join(f"{c ^ p:08b}" for c, p in zip(ct, known))
print(f"[*] ์ํธ๋ฌธ {len(ct)}๋ฐ์ดํธ, ์๋ ค์ง ํค์คํธ๋ฆผ {len(ks)}๋นํธ: {ks}")
printable = set(string.printable[:-5].encode())
for guess in range(0x100):
bits = ks + f"{guess:08b}" # k1..k32
# 32์คํ
๋ค์ ์ํ๋ ๋ฐฉ๊ธ ๋์จ ๋นํธ๊ฐ ์์ ์ค๋๋ก ๋ค์งํ ์์๋ค
state = bits[::-1]
for _ in range(32):
state = unshift(state)
seed = int(state, 2)
flag = STREAM(seed, 32).decrypt(ct)
if flag[:3] != known or flag[-1:] != b"}":
continue
if not set(flag) <= printable:
continue
print(f"[+] seed = {seed} (0x{seed:08x}), 4๋ฒ์งธ ๊ธ์ ํค์คํธ๋ฆผ = {guess:08b}")
print(f"[+] FLAG: {flag.decode()}")
return
print("[-] ํ๋ณด 256๊ฐ ๋ชจ๋ ์คํจ")
if __name__ == "__main__":
main()python3 solve.py
์๋๋ 0xfe5db0a9, ํ๋๊ทธ๋ DH{ ๋ค์ sha256 ์ฒ๋ผ ์๊ธด hex 64๊ธ์๊ฐ ๋ถ์ ํํ๋ค. ์ฒด๊ฐ ์คํ ์๊ฐ์ 0.01์ด์ฏค์ด๋ค.
๐งช ๊ต์ฐจ๊ฒ์ฆ ๋ ๊ฐ์ง
ํ๋๊ทธ๊ฐ ๋์๋ค๊ณ ๋๋ด๋ฉด ์์ฝ๋ค. ์๋๋ฅผ ์ ๋๋ก ๋ณต์ํ ๊ฒ ๋ง๋์ง ๋ ๋ฐฉํฅ์์ ํ์ธํ๋ค.
์ฒซ์งธ, ๋ณต์ํ ์๋๋ก ํ๋๊ทธ๋ฅผ ๋ค์ ์ํธํํด์ ๋ฐฐํฌ๋ output.txt ์ ๋ฐ์ดํธ ๋จ์๋ก ๋์กฐํ๋ค. ๋ณตํธ๊ฐ ์ฐ์ฐํ ๊ทธ๋ด๋ฏํ๊ฒ ๋์จ ๊ฒ ์๋๋ผ ์๋ ์์ฒด๊ฐ ๋ง๋ค๋ ๋ป์ด ๋๋ค.
#!/usr/bin/env python3
"""๋ณต์ํ ์๋๋ก flag ๋ฅผ ๋ค์ ์ํธํํด ๋ฐฐํฌ๋ output.txt ์ ๋ฐ์ดํธ ๋จ์๋ก ๋์กฐํ๋ค."""
import sys
sys.path.insert(0, "extracted")
from cipher import STREAM
SEED = 0xFE5DB0A9
FLAG = b"DH{855083595b1557aeb8f60e7422e49405672b8ddf793700d884dae710909f9d0f}"
ct = bytes.fromhex(open("extracted/output.txt").read().split(">")[1].strip())
mine = STREAM(SEED, 32).encrypt(FLAG)
print(f"[*] ๋ฐฐํฌ๋ ์ํธ๋ฌธ : {ct.hex()[:48]}... ({len(ct)}๋ฐ์ดํธ)")
print(f"[*] ์ฌ์ํธํ ๊ฒฐ๊ณผ : {mine.hex()[:48]}... ({len(mine)}๋ฐ์ดํธ)")
print(f"[+] ์์ ์ผ์น? {mine == ct}")
print(f"[+] ๋ณตํธ ๊ฒฐ๊ณผ : {STREAM(SEED, 32).decrypt(ct).decode()}")python3 verify.py
๋ฌธ์ ๊ฐ ์ค cipher.py ๋ฅผ ๊ทธ๋๋ก import ํด์ ๋๋ ธ๋ค. ์ฐ๋ฆฌ ๊ตฌํ์ด ์๋๋ผ ์ถ์ ์ ๊ตฌํ์ผ๋ก ์ฌ์ํธํํ ๊ฒฐ๊ณผ๊ฐ ์ผ์นํ๋ค.
๋์งธ๋ SageMath ์ชฝ์ด๋ค. ์ฃผ์์ ์ ํ ๋คํญ์์ด ์ ๋ง ์์์ธ์ง, ๊ทธ๋ฆฌ๊ณ ์ฝ๋๊ฐ ์ค์ ๋ก ๋๋ ์ ํ์์ด ๋ฌด์์ธ์ง ํ์ธํ๋ค.
ํญ ์ธ๋ฑ์ฑ ๋ฐฉ์ ๋๋ฌธ์ ์ฌ๋ฏธ์๋ ์ด๊ธ๋จ์ด ํ๋ ์๋ค. ์ฝ๋๊ฐ ๋๋ ์ ํ์์ k_t = k_(t-1) + k_(t-2) + k_(t-22) + k_(t-32) ์ธ๋ฐ, ์ด๊ฑด ์ฃผ์์ x^32 + x^22 + x^2 + x + 1 ์ด ์๋๋ผ ๊ทธ ์ญ์ ๋คํญ์ x^32 + x^31 + x^30 + x^10 + 1 ์ ํด๋นํ๋ค. ์์ ๋คํญ์์ ์ญ์๋ ์์๋ผ์ ์ฃผ๊ธฐ๋ ๋๊ฐ์ด 2^32 โ 1 ์ด๋ค. ๊ทธ๋ฌ๋ ์ค์ ์์ ๋ฌธ์ ๊ฐ ๋์ง๋ ์๋๋ค.
๋ง๋ก๋ง ํ๋ฉด ๋ฏฟ๊ธฐ ์ด๋ ค์ฐ๋ ๋ณต์ํ ํค์คํธ๋ฆผ 544๋นํธ์ BerlekampโMassey ๋ฅผ ๊ฑธ์ด ์ต์ ๋คํญ์์ ๋ฝ์๋ค.
from sage.matrix.berlekamp_massey import berlekamp_massey
R.<x> = GF(2)[]
f = x^32 + x^22 + x^2 + x + 1 # cipher.py ์ฃผ์์ ๋คํญ์
g = f.reverse() # ์ฝ๋๊ฐ ์ค์ ๋ก ๋๋ ์ ํ์
print("[1] ์ฃผ์ ๋คํญ์ f =", f)
print(" ๊ธฐ์ฝ(irreducible)?", f.is_irreducible(), " ์์(primitive)?", f.is_primitive())
print(" โ ์ฃผ๊ธฐ 2^32-1 =", 2^32 - 1)
print()
print("[2] ์ญ์ ๋คํญ์ g =", g)
print(" ์์(primitive)?", g.is_primitive())
print(" ์ฝ๋์ ์ ํ์: k_t = k_(t-1) + k_(t-2) + k_(t-22) + k_(t-32)")
print()
flag = b'DH{855083595b1557aeb8f60e7422e49405672b8ddf793700d884dae710909f9d0f}'
ct = bytes.fromhex(open('extracted/output.txt').read().split('>')[1].strip())
bits = ''.join(format(c ^^ p, '08b') for c, p in zip(ct, flag))
seq = [GF(2)(int(b)) for b in bits]
print("[3] ๋ณต์ํ ํค์คํธ๋ฆผ", len(seq), "๋นํธ์ Berlekamp-Massey")
bm = berlekamp_massey(seq)
print(" ์ต์ ๋คํญ์ =", bm, " (์ฐจ์", bm.degree(), ")")
print(" == g ?", bm == g, " == f ?", bm == f)sage lfsr_poly.sage
BM ์ด ๋๋ ค์ค ์ต์ ๋คํญ์์ ์ฐจ์ 32, ๊ฐ์ x^32 + x^31 + x^30 + x^10 + 1. ์ญ์ ๋คํญ์๊ณผ ์ ํํ ๊ฐ๋ค. ํค์คํธ๋ฆผ๋ง ๋ณด๊ณ ๋ LFSR ์ ๊ธธ์ด์ ํญ์ด ๋ณต์๋๋ค๋ ๋ป์ด๊ณ , ์ด๊ฒ LFSR ์ ํค์คํธ๋ฆผ ์์ฑ๊ธฐ๋ก ์ธ ๋ ์ง์ง ๋ฌด์์ด ์ง์ ์ด๋ค. ์๋ ค์ง ํ๋ฌธ์ด 64๋นํธ๋ง ์์ผ๋ฉด BM ํ๋๋ก ๋๋จธ์ง ์ ๋ถ๊ฐ ์์ธก๋๋ค. ์ฐ๋ฆฌ๋ DH{ 24๋นํธ๋ฐ์ ์์ด์ 8๋นํธ๋ฅผ ํ์์ ๋ฟ์ด๋ค.
โป๏ธ ์ฌํ
๋ฐฐํฌ๋ณธ๊ณผ ์คํฌ๋ฆฝํธ๋ง ์์ผ๋ฉด ์๋ฒ ์์ด ์ธ์ ๋ ๋ค์ ๋์๊ฐ๋ค.
#!/usr/bin/env bash
# STREME REVERIE (DreamHack #1923, Bronze 2, crypto) โ ํ ๋ฐฉ ์ฌํ.
# ๋ฐฐํฌ๋ extracted/output.txt ๋ง์ผ๋ก flag ๋ฅผ ๋ณต์ํ๊ณ ์ ๋ต๊ณผ ๋์กฐํ๋ค. ์๋ฒ ๋ถํ์.
set -eu
cd "$(dirname "$(readlink -f "$0")")"
EXPECT=$(python3 -c "import json;print(json.load(open('๋ฌธ์ .json'))['flag'])" 2>/dev/null || echo '')
OUT=$(timeout 300 python3 solve.py "$@" 2>&1) || true
echo "$OUT" | tail -8
FLAG=$(printf '%s' "$OUT" | grep -aoE 'DH\{[^}]+\}' | head -1)
if [ -n "$FLAG" ] && { [ -z "$EXPECT" ] || [ "$FLAG" = "$EXPECT" ]; }; then
echo; echo "โ
PASS $FLAG"; exit 0
fi
echo; echo "โ FAIL (์ป์ ๊ฐ: '${FLAG:-์์}' / ๊ธฐ๋: '$EXPECT')"; exit 1./reproduce.sh
๋ก์ปฌ ํ๊ฒฝ์ Ubuntu 25.10 ยท Python 3.13 ยท SageMath 10.9 ๋ค. ์คํฌ๋ฆฝํธ๋ ์ ๋ถ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ง ์ฐ๊ณ , sage ๋ ๊ต์ฐจ๊ฒ์ฆ์๋ง ํ์ํ๋ค.
๐ ๊ฒฐ๋ก
LFSR ์ ์ถ๋ ฅ์ด ๊ณง ์ํ๋ค
์ด ๋ฌธ์ ์ ์ทจ์ฝ์ ์ ํญ ์์น๋ ์๋ ํฌ๊ธฐ๊ฐ ์๋๋ผ ๊ตฌ์กฐ ์์ฒด์ ์๋ค. ํผ๋ณด๋์นํ LFSR ์ ์๋ก ๋ง๋ ๋นํธ๋ฅผ ๋ ์ง์คํฐ์ ๋ฃ์ผ๋ฉด์ ๋์์ ๋ฐ์ผ๋ก ๋ด๋ณด๋ธ๋ค. ๋ ์ง์คํฐ ๊ธธ์ด๋งํผ์ ์ถ๋ ฅ์ ๊ด์ธกํ๋ฉด ๊ทธ ์์ ์ ๋ด๋ถ ์ํ๊ฐ ๊ทธ๋๋ก ๋ ธ์ถ๋๋ค. ์๋๊ฐ 32๋นํธ๋ 128๋นํธ๋ ๋ง์ฐฌ๊ฐ์ง๊ณ , ๊ธธ์ด๋ฅผ ๋๋ฆฌ๋ฉด ๊ด์ธกํด์ผ ํ ๋นํธ๋ง ๋์ด๋๋ค.
์ฃผ๊ธฐ์ ์์ธก ๋ถ๊ฐ๋ฅ์ฑ์ ๋ค๋ฅธ ์ด์ผ๊ธฐ๋ค
์ฃผ์์ ๋คํญ์์ ์์์๊ณ ์ฃผ๊ธฐ๋ 2^32 โ 1 ์ด์๋ค. ํต๊ณ ๊ฒ์ ์ ํต๊ณผํ ๋งํผ ๋๋คํด ๋ณด์ธ๋ค๋ ๋ป์ด์ง๋ง, ๊ทธ๊ฑด ์์ธก์ ๋ชป ํ๋ค๋ ๋ณด์ฅ์ด ์๋๋ค. BerlekampโMassey ๋ ๊ธธ์ด L ์ธ LFSR ์ ์ถ๋ ฅ 2L ๋นํธ๋ง ์์ผ๋ฉด ๊ทธ LFSR ์ ํต์งธ๋ก ๋ณต์ํ๋ค. ์ด ๋ฌธ์ ์์๋ 64๋นํธ๋ฉด ์ถฉ๋ถํ๋ค.
์๋ ค์ง ํ๋ฌธ ์ธ ๊ธ์๊ฐ ๋๋ถ๋ถ์ ํด์คฌ๋ค
assert flag[:3] == b'DH{' ํ ์ค์ด 24๋นํธ๋ฅผ ๊ณต์ง๋ก ๋๊ฒจ์คฌ๊ณ , ๋จ์ 8๋นํธ๋ง ํ์ผ๋ฉด ๋๋ค. ํ๋๊ทธ ํฌ๋งท์ด ๊ณ ์ ๋ CTF ๋ฌธ์ ์์๋ ์๋จธ๋ฆฌ ๋ช ๋ฐ์ดํธ๊ฐ ํญ์ ๊ณต์ง ํ๋ฌธ์ด๋ค. ์คํธ๋ฆผ ์ํธ ๋ฌธ์ ๋ฅผ ๋ง๋ฌ์ ๋ ์ ์ผ ๋จผ์ ๊ณ์ฐํด ๋ณผ ๊ฒ์ด ์ด "๊ณต์ง ํค์คํธ๋ฆผ"์ ๊ธธ์ด๋ค.
๊ทธ๋์ ์ค๋ฌด์์๋
LFSR ์ ๊ตณ์ด ์จ์ผ ํ๋ค๋ฉด ์ถ๋ ฅ์ ๊ทธ๋๋ก ์ฐ์ง ์๋๋ค. ์ฌ๋ฌ LFSR ์ ์ถ๋ ฅ์ ๋น์ ํ ํจ์๋ก ์๊ฑฐ๋(A5/1, E0), ํด๋ญ์ ๋ถ๊ท์นํ๊ฒ ํ๊ฑฐ๋, ํํฐ ํจ์๋ฅผ ์์ ์ ํ์ฑ์ ๊นฌ๋ค. ๋ค๋ง ๊ทธ๋ ๊ฒ ๋ง๋ ๋ฐฉ์๋ค๋ ํ๋์ฉ ๊นจ์ ธ ์๋ค. ์ง๊ธ ์๋ก ์ค๊ณํ๋ค๋ฉด ChaCha20 ๊ฐ์ ๊ฒ์ฆ๋ ์คํธ๋ฆผ ์ํธ๋ฅผ ์ฐ๋ ์ชฝ์ด ๋ง๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ