CodeShell.kr - Fold

Challenge

一份折叠说明,规格给得很完整。难点在于置换搜索空间要先用约束剪掉,再用哈希唯一确认。

Unfold the note

把纸条展开。

1
https://codeshell.kr/challenges/fold/

Solution

附件 fold-v1.txt 给出完整算法规格:一个 8 元置换 p、六条加权和约束、sha256(bytes(p)) 校验值,以及用 SHA-256 派生 keystream 的块加密流程,载荷为 ASCII 并做了 PKCS#7 填充。

Step 1:8! = 40320 全枚举,用六条约束过滤后用 sha256 唯一确认:

1
p = (5, 2, 7, 0, 3, 6, 1, 4)

Step 2:按规格解密两个 8 字节块(x[j] = block[p[j]],再与 SHA256("FOLD/v1" || p || n)[:8] 异或):

1
2
n=0 -> FOLDED_M
n=1 -> IRROR\x03\x03\x03

拼接为 FOLDED_MIRROR 加 3 字节 PKCS#7 填充,填充合法,说明置换与实现均正确。

Script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#!/usr/bin/env python3
"""CodeShell.kr — Fold (Crypto, 100p) solver.

Spec (fold-v1.txt):
p is a permutation of [0..7];
for i=0..5: sum((i+1)*(j+1)*p[j] for j) % 257 == targets[i];
sha256(bytes(p)) confirms the permutation.
stream = SHA256(b"FOLD/v1" || bytes(p) || byte(n))[:8]
x = ciphertext block with bytes placed back from positions p[j]
plaintext[j] = x[j] XOR stream[j]

Usage: uv run python solvers/fold.py
"""

import hashlib
import itertools

TARGETS = [120, 240, 103, 223, 86, 206]
SHA = "97b15666263129bbdf26fc150504e3296abf6c761174b3f164d80250539e25a1"
CT = bytes.fromhex("E31C813F0D9F296325B81E02E1439EB2")


def find_perm():
for p in itertools.permutations(range(8)):
if all(
sum((i + 1) * (j + 1) * p[j] for j in range(8)) % 257 == TARGETS[i]
for i in range(6)
):
if hashlib.sha256(bytes(p)).hexdigest() == SHA:
return p
raise SystemExit("no permutation matched")


def decrypt(p, ct):
out = bytearray()
for n in range(len(ct) // 8):
block = ct[n * 8:(n + 1) * 8]
stream = hashlib.sha256(b"FOLD/v1" + bytes(p) + bytes([n])).digest()[:8]
x = bytearray(8)
for j in range(8):
x[j] = block[p[j]] # x[j] takes the byte at position p[j]
out += bytes(a ^ b for a, b in zip(x, stream))
return bytes(out)


def main():
p = find_perm()
print(f"perm={p} sha256-ok")
pt = decrypt(p, CT)
# strip PKCS#7
pad = pt[-1]
assert 1 <= pad <= 8 and pt[-pad:] == bytes([pad]) * pad
print(f"plaintext: {pt!r} -> {pt[:-pad].decode()}")


if __name__ == "__main__":
main()
CodeShell{FOLDED_MIRROR}