defencrypt(pt): cipher = AES.new(key=key, mode=AES.MODE_ECB) blocks = [pt[i:i+BLOCK_SIZE] for i inrange(0, len(pt), BLOCK_SIZE)] tmp = iv ret = b"" for block in blocks: res = cipher.encrypt(xor(block, tmp)) ret += res tmp = xor(block, res) return ret
defdecrypt(ct): cipher = AES.new(key=key, mode=AES.MODE_ECB) blocks = [ct[i:i+BLOCK_SIZE] for i inrange(0, len(ct), BLOCK_SIZE)]
for block in blocks: if block in secret_enc: blocks.remove(block) tmp = iv ret = b"" for block in blocks: res = xor(cipher.decrypt(block), tmp) ret += res tmp = xor(block, res) return ret secret = os.urandom(80) secret_enc = encrypt(secret)
print(f"Encrypted secret: {secret_enc.hex()}")
print("Enter messages to decrypt (in hex): ")
whileTrue: res = input("> ")
try: enc = bytes.fromhex(res)
if (enc == secret_enc): print("Nice try.") continue dec = decrypt(enc) if (dec == secret): print(f"Wow! Here's the flag: {flag}") break
else: print(dec.hex()) except Exception as e: print(e) continue
from Crypto.Cipher import AES from Crypto.Util.Padding import pad from hashlib import md5 import os
withopen("flag.txt", "r") as f: flag = f.read()
BLOCK_SIZE = 16 iv = os.urandom(BLOCK_SIZE)
xor = lambda x, y: bytes(a^b for a,b inzip(x,y))
key = os.urandom(16)
defencrypt(pt): cipher = AES.new(key=key, mode=AES.MODE_ECB) blocks = [pt[i:i+BLOCK_SIZE] for i inrange(0, len(pt), BLOCK_SIZE)] tmp = iv ret = b"" for block in blocks: res = cipher.encrypt(xor(block, tmp)) ret += res tmp = xor(block, res) return ret
defdecrypt(ct): cipher = AES.new(key=key, mode=AES.MODE_ECB) blocks = [ct[i:i+BLOCK_SIZE] for i inrange(0, len(ct), BLOCK_SIZE)]
tmp = iv ret = b"" for block in blocks: res = xor(cipher.decrypt(block), tmp) if (res notin secret): ret += res tmp = xor(block, res) return ret secret = os.urandom(80) secret_enc = encrypt(secret)
idx_map = {} for i inrange(16): pt = bytearray(b"a" * 16) ct1 = aes.encrypt(pt) pt[i] ^= 1 ct2 = aes.encrypt(pt) diff_idx = next(i for i, (a, b) inenumerate(zip(ct1, ct2)) if a != b) idx_map[i] = diff_idx print(idx_map)
pts = [bytes([i]) * 16for i inrange(256)] pt = b"".join(pts) io.sendline(pt.hex().encode()) io.recvuntil(b"c: ") ct = bytes.fromhex(io.recvlineS().strip()) cts = [ct[i : i + 16] for i inrange(0, len(ct), 16)]
enc_map = {} # (idx, val) -> (idx, val) dec_map = {} # (idx, val) -> (idx, val) for pt, ct inzip(pts, cts): for i, a inenumerate(pt): j = idx_map[i] b = ct[j] enc_map[(i, a)] = (j, b) dec_map[(j, b)] = (i, a)
defencrypt(enc_map, pt): ct = [0] * 16 for i, a inenumerate(pt): j, b = enc_map[(i, a)] ct[j] = b returnbytes(ct)
defdecrypt(dec_map, ct): pt = [0] * 16 for j, b inenumerate(ct): i, a = dec_map[(j, b)] pt[i] = a returnbytes(pt)
io.recvuntil(b"c_p: ") pwd_ct = bytes.fromhex(io.recvlineS().strip()) pwd_ct_blks = [pwd_ct[i : i + 16] for i inrange(0, len(pwd_ct), 16)] pwd_pt_blks = [decrypt(dec_map, cblk) for cblk in pwd_ct_blks] pwd = b"".join(pwd_pt_blks)[:16] io.sendline(pwd.hex().encode()) io.interactive() # grey{mix_column_is_important_in_AES_ExB3Hf9q9I3m}
from secrets import token_bytes, randbits from param import A import numpy as np
FLAG = 'REDACTED'
A = np.array(A)
defprint_art(): print(r""" />_________________________________ [########[]_________________________________> \> """) defbytes_to_bits(s): returnlist(map(int, ''.join(format(x, '08b') for x in s)))
defbits_to_bytes(b): returnbytes(int(''.join(map(str, b[i:i+8])), 2) for i inrange(0, len(b), 8))
defprg(length): x = token_bytes(8); r = token_bytes(8); k = token_bytes(8) x = np.array(bytes_to_bits(x)); r = np.array(bytes_to_bits(r)); k = np.array(bytes_to_bits(k)) output = [] for i inrange(length * 8): output.append(sum(x) % 2) if (i % 3 == 0): x = (A @ x + r) % 2 if (i % 3 == 1): x = (A @ x + k) % 2 if (i % 3 == 2): x = (A @ x + r + k) % 2 output = output return bits_to_bytes(output).hex() deftrue_random(length): return token_bytes(length).hex()
defmain(): try: print_art() print("I try to create my own PRG") print("This should be secure...") print("If you can win my security game for 100 times, then I will give you the flag") for i inrange(100): print(f"Game {i}") print("Output: ", end="") game = randbits(1) if (game): print(prg(16)) else: print(true_random(16)) guess = int(input("What's your guess? (0/1): ")) if guess != game: print("You lose") return print(f"Congrats! Here is your flag: {FLAG}") except Exception as e: return
defbytes_to_bits(s): returnlist(map(int, "".join(format(x, "08b") for x in s)))
F = GF(2) Asage = matrix(F, A) PR = PolynomialRing( F, [f"x{i}"for i inrange(64)] + [f"r{i}"for i inrange(64)] + [f"k{i}"for i inrange(64)], ) x = vector(PR.gens()[:64]) r = vector(PR.gens()[64:128]) k = vector(PR.gens()[128:192]) syms = [] for i inrange(16 * 8): syms.append(sum(x)) if i % 3 == 0: x = Asage * x + r if i % 3 == 1: x = Asage * x + k if i % 3 == 2: x = Asage * x + r + k M, _ = Sequence(syms).coefficients_monomials(sparse=False) print(M.dimensions()) print(M.rank())
from IPFE import IPFE, _FeDDH_C from secrets import randbits
FLAG = 'REDACTED'
# Prime from generate_prime() # To save server resource, we use a fix prime p = 16288504871510480794324762135579703649765856535591342922567026227471362965149586884658054200933438380903297812918052138867605188042574409051996196359653039 q = (p - 1) // 2
defgenerate_prime(): whileTrue: q = getPrime(512) p = 2 * q + 1 if isPrime(p): return mpz(p), mpz(q) defdiscrete_log_bound(a, g, bounds, p): cul = pow(g, bounds[0], p) for i inrange(bounds[1] - bounds[0] + 1): if cul == a: return i + bounds[0] cul = (cul * g) % p raise Exception(f"Discrete log for {a} under base {g} not found in bounds ({bounds[0]}, {bounds[1]})")
classIPFE: @staticmethod defgenerate(n: int, prime: Tuple[int, int] = None): if (prime == None): p, q = generate_prime() else: p, q = prime g = mpz(randbelow(p) ** 2) % p msk = [randbelow(q) for _ inrange(n)] mpk = [pow(g, msk[i], p) for i inrange(n)]
return _FeDDH_MK(g, n, p, q, mpk=mpk, msk=msk)
@staticmethod defencrypt(x: List[int], pub: _FeDDH_MK) -> _FeDDH_C: iflen(x) != pub.n: raise Exception("Encrypt vector must be of length n") r = randbelow(pub.q) g_r = pow(pub.g, r, pub.p) c = [(pow(pub.mpk[i], r, pub.p) * pow(pub.g, x[i], pub.p)) % pub.p for i inrange(pub.n)]
return _FeDDH_C(g_r, c) @staticmethod defdecrypt(c: _FeDDH_C, pub: _FeDDH_MK, sk: _FeDDH_SK, bound: Tuple[int, int]) -> int: cul = 1 for i inrange(pub.n): cul = (cul * pow(c.c[i], sk.y[i], pub.p)) % pub.p cul = (cul * inverse(sk.sk, pub.p)) % pub.p return discrete_log_bound(cul, pub.g, bound, pub.p) @staticmethod defkeygen(y: List[int], key: _FeDDH_MK, c: _FeDDH_C) -> _FeDDH_SK: iflen(y) != key.n: raise Exception(f"Function vector must be of length {key.n}") ifnot key.has_private_key(): raise Exception("Private key not found in master key") t = sum([key.msk[i] * y[i] for i inrange(key.n)]) % key.q sk = pow(c.g_r, t, key.p) return _FeDDH_SK(y, sk) if __name__ == "__main__": n = 10 key = IPFE.generate(n) x = [i for i inrange(n)] y = [i + 10for i inrange(n)] c = IPFE.encrypt(x, key) sk = IPFE.keygen(y, key, c) m = IPFE.decrypt(c, key.get_public_key(), sk, (0, 1000)) expected = sum([a * b for a, b inzip(x, y)]) assert m == expected
總之這題用 IPFE,一個 functional encryption for inner product 的
scheme 弄了一個題目。目標是得到 encrypt 的結果之後可以恢復原本的
challenge。本身是在一個 中的一個 prime order
subgroup 做的,order 和 generator 記為 。
defrecover(oracle, idx): g_r = 7 assertpow(g_r, q, p) != 1 a = pow(2, -1, q) bits = [] for i inrange(q.bit_length()): y = [0, 0, 0, 0, 0] y[idx] = pow(2, -i, q) r = int(pow(oracle(g_r, y), q, p) != 1) # lsb k = sum((pow(a, i + 1, q) * b) % q for i, b inenumerate(bits[::-1])) % q bits.append((r - k) % 2) returnsum(b << i for i, b inenumerate(bits))
defbatch_oracle(g_r_ar, y_ar): for g_r, y inzip(g_r_ar, y_ar): io.sendline(b"2") io.sendline(" ".join(map(str, y)).encode()) io.sendline(str(g_r).encode()) sk_ar = [] for _ inrange(len(g_r_ar)): io.recvuntil(b"s_k: ") sk_ar.append(int(io.recvlineS().strip())) return sk_ar
defrecover_batch(batch_oracle, idx): g_r = 7 assertpow(g_r, q, p) != 1 a = pow(2, -1, q) ys = [] for i inrange(q.bit_length()): y = [0, 0, 0, 0, 0] y[idx] = pow(2, -i, q) ys.append(y) sk_ar = batch_oracle([g_r] * len(ys), ys) bits = [] for i inrange(q.bit_length()): r = int(pow(sk_ar[i], q, p) != 1) # lsb k = sum((pow(a, i + 1, q) * b) % q for i, b inenumerate(bits[::-1])) % q bits.append((r - k) % 2) returnsum(b << i for i, b inenumerate(bits))
msk = [] for i inrange(n): # sk = recover(oracle, i) sk = recover_batch(batch_oracle, i) assertpow(g, sk, p) == mpk[i] msk.append(sk) print(i, sk)
from sage.allimport * from sage.coding.linear_code import LinearCode from sage.coding.information_set_decoder import LeeBrickellISDAlgorithm from pwn import process, remote, context import signal
n = 100 k = int(n * 2) threshold = 0.05 F = GF(2)
defbits_to_matrix(s): assertlen(s) == n * k return matrix(F, [[int(s[i * k + j]) for j inrange(k)] for i inrange(n)])
from param import p, b, n from secrets import randbelow from hashlib import shake_128
defencrypt(msg, key): y = shake_128("".join(map(str, key)).encode()).digest(len(msg)) returnbytes([msg[i] ^^ y[i] for i inrange(len(msg))]) FLAG = b'REDACTED' m = 150
factor = [randbelow(2) for _ inrange(m)] k = sum([hidden[i] * factor[i] for i inrange(m)]) % 2 factors.append(factor) if (k): x, y, z = [randbelow(n) for _ inrange(3)] else: x, y = [randbelow(n) for _ inrange(2)] z = x * y
output.append([g, x * g, y * g, z * g, h, x * h])
output = [[(point[0], point[1]) for point in row] for row in output]
f = open("output.txt", "w") f.write(f"c='{encrypt(FLAG, hidden).hex()}'\n") f.write(f"{factors=}\n") f.write(f"{output=}\n")
這題是這場 CTF 最有趣也最難的 crypto 題目。首先有
還有隨機的 。
其中 固定, 是隨機的。之後
factor, hidden 那邊是個 的 linear system
可以先不管它,只需要關注 或
的情況,會有 or 的情況。
然後它會提供給你 ,要想辦法去 distinguish
or 的 case 去得到 ,然後就可以解 linear system 得到
hidden。
p = 2956673455706017732726395787961404603421884201335599271480572766387023983052387933523497453145098834977111426152922969191412599940148797425165972377716091055492258826885933065623708772008210452334640417645070465335307327936488470721870990368244784905723647386940034701846717718881287895717648756082302396599141 n = 2956673455706017732726395787961404603421884201335599271480572766387023983052387933523497453145098834977111426152922969191412599940148797425165972377716091001116956936174395309176601513634561168168290944226676456373026396891854015965003822945171727063054140667291090754603076704996926282408059289968479821894541 b = 6
defencrypt(msg, key): y = shake_128("".join(map(str, key)).encode()).digest(len(msg)) returnbytes([msg[i] ^^ y[i] for i inrange(len(msg))])
proof.arithmetic(False)
p = 2956673455706017732726395787961404603421884201335599271480572766387023983052387933523497453145098834977111426152922969191412599940148797425165972377716091055492258826885933065623708772008210452334640417645070465335307327936488470721870990368244784905723647386940034701846717718881287895717648756082302396599141 n = 2956673455706017732726395787961404603421884201335599271480572766387023983052387933523497453145098834977111426152922969191412599940148797425165972377716091001116956936174395309176601513634561168168290944226676456373026396891854015965003822945171727063054140667291090754603076704996926282408059289968479821894541 b = 6