defcrcmat(crc, n, m): """ Recover matrix of crc by black box crc: crc function, bytes->int n: crc output bits m: crc input bits crc(x) = Ax+C x is n bits crc(x) is m bits A is m by n matrix Assuming bits reversed crc """ C = vector(GF(2), i2v(crc(v2b([0] * m)), n)) right = [] # left = [] for i inrange(m): v = [0] * m v[i] = 1 # left.append(v) right.append(vector(i2v(crc(v2b(v)), n)) - C) # L = matrix(GF(2), left).T R = matrix(GF(2), right).T # A = R * L.inverse() # Note: L is identity matrix A = R return A, C
deffind_append(crc, nb, msg, target): A, C = crcmat(crc, nb * 8, (len(msg) + nb) * 8) CC = A * vector(b2v(msg + b"\x00" * nb)) + C t = vector(i2v(target, nb * 8)) sol = A[:, : nb * 8].solve_right(t - CC) assert crc(msg + v2b(sol)) == target return v2b(sol)
if __name__ == "__main__": # matrix recover test A, C = crcmat(crc32, 32, 128) msg = bytearray(b"x" * (128 // 8)) msg[-8:] = b"pekomiko" x = v2i(A * vector(b2v(msg)) + C) assert x == crc32(msg)
# find crc(x) == x for 32 bits x # A*x+C=x # (A-I)*x=-C A, C = crcmat(crc32, 32, 32) x = v2b((A - matrix.identity(32)).solve_right(-C)) assert v2i(b2v(x)) == crc32(x)
for seeds inrange(1000): h = str(seeds) payload = script + h c = crc32(payload.encode()) cb = (0xffffffff ^ c).to_bytes(4, "little") ifall(b < 128andchr(b).isalnum() for b in cb): blob = payload.encode() + cb print(len(blob), repr(blob)) print(hex(crc32(blob)))
# without this test_add_mixed fails... defadd(a, b): return a + b
defnoop(*args, **kwargs): return
for k in sys.modules: if k.startswith("test"): mod = sys.modules[k] for cname indir(mod): if cname.startswith("Test"): cls = getattr(mod, cname) for fname indir(cls): if fname.startswith("test"): setattr(cls, fname, noop)
if (code) { const parsed = HTMLParser.parse(code); for (let img of parsed.getElementsByTagName('img')) { let src = img.getAttribute('src'); if (src) { images.push(src); } } }
from sage.allimport * from pwn import remote from tqdm import tqdm from Crypto.Util.number import * from Crypto.Cipher import PKCS1_OAEP from Crypto.PublicKey import RSA
n = reduce(gcd, [x**17 - enc(x) for x inrange(2**512, 2**512 + 5)]) print(n) # hoping that (p-1)%17==0 so it will be necessary to calculate the primitive root of p before calculating x as x^e=2^17 (mod n) # then there will be 17 x such that x^e=2^17 (mod p), but only 1 x such that x^e=2^17 (mod q) # therefore x = 2 (mod q) but x = ? (mod p), then we can get q by gcd(x-2, n) x = dec(2**17) p = gcd(x - 2, n) if p == 1or p == n: exit(1) q = n // p c = flag()
defoaep_unpad(x, n): whileTrue: key = RSA.generate(1024) if key.n > n: break c = pow(x, key.e, key.n) c = long_to_bytes(c, (key.n.bit_length() + 7) // 8) cipher = PKCS1_OAEP.new(key) return cipher.decrypt(c)
for xp in GF(p)(c).nth_root(17, all=True): for xq in GF(q)(c).nth_root(17, all=True): m = int(crt([ZZ(xp), ZZ(xq)], [p, q])) try: flag = oaep_unpad(m, n) print(flag) except: pass # while true; do python solve.py; ret=$?; [ $ret -eq 0 ] && break; done # dice{rabin-williams-cryptosystem-in-disguise-3038e78caa07}
from sage.allimport * from Crypto.Util.number import bytes_to_long, getPrime from random import randint from math import gcd from os import urandom from pwn import process, remote, context
context.log_level = "debug"
# io = process(["python", "bbbb.py"]) io = remote("mc.ax", 31340) io.recvuntil(b"b=") b = int(io.recvline().strip().decode()) io.recvuntil(b"p=") p = int(io.recvline().strip().decode())
F = Zmod(p) E = 11 P = F["x,a"] x, a = P.gens() f = a * x + b g = f(f(f(f(f, a), a), a), a)(E, a) - E print(g) aa = g.univariate_polynomial().roots(multiplicities=False) print(aa)
for a in aa: print("=" * 40) P = F["x"] x = P.gen() f = a * x + b v = E seeds = set() for i inrange(10): print(i, v) seeds.add(int(v)) v = f(v) if check_seeds(seeds): break print(f"{a = }") print(f"{seeds = }") ifnot check_seeds(seeds): print("bad seeds :(") exit(1)
io.sendlineafter(b": ", str(a).encode()) for seed in seeds: io.sendlineafter(b": ", str(seed).encode())
ct = [] for _ inrange(5): io.recvuntil(b"Public Key:") io.recvuntil(b"n=") n = int(io.recvline().strip()) io.recvuntil(b"e=") e = int(io.recvline().strip()) io.recvuntil(b"r=") r = io.recvlineS().strip()[1:-1] io.recvuntil(b"Cipher Text: ") c = int(io.recvline().strip()) if e != E: print("bad e :(", _) exit(1) ct.append((n, e, r, c))
P = ZZ["x"] x = P.gen() ff = [] nn = [] for n, e, r, c in ct: rn = int(r, 16) f = (x * 2**128 + rn) ** e - c ff.append(f) nn.append(ZZ(n)) f = crt(ff, nn) M = product(nn) f = f.change_ring(Zmod(M)).monic() print(f) print(M) x = int(f.small_roots(epsilon=0.05)) print(long_to_bytes(x)) # while true; do python solve.py; ret=$?; [ $ret -eq 0 ] && break; done # dice{r3s0rt_t0_LCG_4ft3r_f41l1ng_t0_m4k3_ch4ll}
from pwn import * from Crypto.Util.strxor import strxor from Crypto.Hash import SHAKE128
PRIVATE_KEY_SIZE = 74 PUBLIC_KEY_SIZE = 64 p = 5326738796327623094747867617954605554069371494832722337612446642054009560026576537626892113026381253624626941643949444792662881241621373288942880288065659
definvert_pub(pub): x = int.from_bytes(bytes(pub), "little") return (p - x).to_bytes(PUBLIC_KEY_SIZE, "little")
defstream(buf, ss): pad = SHAKE128.new(bytes(ss)).read(len(buf)) return strxor(buf, pad)
""" Explanation of the protocol: We have base ---------> pub0 | priv0 | |priv1 | v pub1 Then we are supposed to find a secret isogeny to map either pub0 or pub1 to mask, take pub1 for example base ---------> pub0 | priv0 | |priv1 | v iso pub1 -----------------> mask Then Alice when take apply priv0^-1 and priv1^-1 to iso to get two shared secrets, then encrypt the messages respectively. Attack: If we take mask=base, then the shared secret will be apply_iso(base, priv0^-1) and apply_iso(base, priv1^-1), but CSIDH isn't broken (like SIDH) so we can't get the private keys. Fortunately, I found that public keys (the A component of y^2=x^3+Ax^2+x) satisfy (apply_iso(base, priv)+apply_iso(base, priv^-1))=0 (mod p) for some reason. So we can easily compute the final shared secret by computing additive inverse. """
不過 sage 的 quadratic_twist 計算方法好像和 wiki
上寫的不太一樣,所以還要後面接 montgomery_model
才能得到我們想要的 curve 格式。另外是在 的 finite field 下 quadratic twist
的 其實不重要。(sage
doc)
所以利用 sage 可以把 invert_pub 改寫為:
1 2 3 4 5 6 7 8 9 10
p = 5326738796327623094747867617954605554069371494832722337612446642054009560026576537626892113026381253624626941643949444792662881241621373288942880288065659 F = GF(p)
definvert_pub(pub): A = int.from_bytes(bytes(pub), "little") E = EllipticCurve(F, [0, A, 0, 1, 0]) Ei = E.quadratic_twist().montgomery_model() A2 = int(Ei.a2()) return A2.to_bytes(PUBLIC_KEY_SIZE, "little")
import numpy as np import random from itertools import product from concurrent.futures import ProcessPoolExecutor
n = 512 # number of public key samples m = n + 100 # plaintext modulus p = 257 # ciphertext modulus q = 1048583 F = GF(q) pinv = inverse_mod(p, q)
x = np.load("data.npz") pk_A = matrix(F, x["pk_A"]) pk_b = vector(F, x["pk_b"]) encrypt_A = [vector(F, y) for y in x["encrypt_A"]] encrypt_b = [F(y) for y in x["encrypt_b"]]
defsolve(L, sel): R = L[:, sel].LLL() for row in R: if row != 0: return row returnNone
defget_sols(L, t=150): # L is pretty big, so we sample random t columns to improve running time # t=150 is chosen by trial and error... cols = list(range(m)) random.shuffle(cols) sols = [] for i inrange(0, m, t): sel = cols[i : i + t] iflen(sel) < t: sel = cols[-t:] whileTrue: s = solve(L, sel) print("get_sols", i, s) ifall([-1 <= x <= 1for x in s]): break print("Failed, sample more columns and try again") sel = sel + list(random.sample(cols, 10)) sols.append((sel, s)) return sols
defdecrypt_with_c(a, b, c): m_pe = ZZ(b - c * pk_b) if m_pe > q // 2: m_pe -= q m = m_pe % p e = (m_pe - m) // p # we check e because c may be wrong if -10 <= e <= 10: return m
defdecrypt(a, b): sol = pk_A.solve_left(a) ker = pk_A.left_kernel().basis_matrix() # assert sol * pk_A == encrypt_A[0] # assert (sol + ker[0]) * pk_A == encrypt_A[0] # so we use LLL to find c L = ker.stack(sol).change_ring(ZZ) L = L.stack(matrix.identity(m) * q) sols = get_sols(L) # the solution may be negative, so we try all possible signs ret = [] for signs in product((1, -1), repeat=len(sols)): c_vec = vector([0] * m) for sign, (sel, s) inzip(signs, sols): ss = sign * s for i, x inenumerate(sel): c_vec[x] = ss[i] r = decrypt_with_c(a, b, c_vec) if r isnotNone: ret.append(r) iflen(ret) == 0: print("Failed to find a solution, try again") return decrypt(a, b) return ret
defdecrypt_ith(i): print(i, "started", flush=True) m = decrypt(encrypt_A[i], encrypt_b[i]) print(i, "finished", m, flush=True) return m
with ProcessPoolExecutor(max_workers=4) as executor: ms = list(executor.map(decrypt_ith, range(len(encrypt_b)))) print(ms)
# let's guess! bad = [b"\\", b'"', b"/", b"#", b"\r"] for m in product(*ms): ifall([20 <= x < 127for x in m]): f = bytes(m) if f.isascii() andnotany([x in f for x in bad]): print(f) # dice{public-key-learning-with-ease_bd2ffac0592e}
import numpy as np import random from itertools import product from concurrent.futures import ProcessPoolExecutor
n = 512 # number of public key samples m = n + 100 # plaintext modulus p = 257 # ciphertext modulus q = 1048583 F = GF(q) pinv = inverse_mod(p, q)
x = np.load("data.npz") pk_A = matrix(F, x["pk_A"]) pk_b = vector(F, x["pk_b"]) encrypt_A = [vector(F, y) for y in x["encrypt_A"]] encrypt_b = [F(y) for y in x["encrypt_b"]]
defsolve(L, sel): # Kannan's embedding technique, and to ensure the sign is correct L2 = L[:, sel].augment(vector([0] * (m - n) + [1] + [0] * m)) R = L2.LLL() for row in R: if row != 0: if row[-1] == -1: row = -row return row[:-1] returnNone
defget_sols(L, t=153): # L is pretty big, so we sample random t columns to improve running time # t=153 because t*4=m cols = list(range(m)) random.shuffle(cols) sols = [] for i inrange(0, m, t): sel = cols[i : i + t] iflen(sel) < t: sel = cols[-t:] whileTrue: s = solve(L, sel) print("get_sols", i, s) ifall([-1 <= x <= 1for x in s]): break print("Failed, sample more columns and try again") sel = sel + list(random.sample(cols, 10)) sols.append((sel, s)) return sols
defdecrypt_with_c(a, b, c): m_pe = ZZ(b - c * pk_b) if m_pe > q // 2: m_pe -= q m = m_pe % p e = (m_pe - m) // p # we check e because c may be wrong if -10 <= e <= 10: return m
defdecrypt(a, b): sol = pk_A.solve_left(a) ker = pk_A.left_kernel().basis_matrix() # assert sol * pk_A == encrypt_A[0] # assert (sol + ker[0]) * pk_A == encrypt_A[0] # so we use LLL to find c L = ker.stack(sol).change_ring(ZZ) L = L.stack(matrix.identity(m) * q) sols = get_sols(L) ret = [] c_vec = vector([0] * m) for sel, s in sols: for i, x inenumerate(sel): c_vec[x] = s[i] return decrypt_with_c(a, b, c_vec)
defdecrypt_ith(i): print(i, "started", flush=True) m = decrypt(encrypt_A[i], encrypt_b[i]) print(i, "finished", m, flush=True) return m
with ProcessPoolExecutor(max_workers=4) as executor: ms = list(executor.map(decrypt_ith, range(len(encrypt_b)))) print(ms) print(bytes(ms)) # dice{public-key-learning-with-ease_bd2ffac0592e}
Misc
Pike
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#!/usr/local/bin/python
import rpyc from rpyc.utils.server import ThreadedServer
classCalcService(rpyc.Service): defexposed_add(self, l, r): return l + r defexposed_sub(self, l, r): return l - r defexposed_mul(self, l, r): return l * r defexposed_div(self, l, r): return l / r
if __name__ == "__main__": server = ThreadedServer(CalcService, port=9999) server.start()