# WU Crypto
## Blocky 0
Sources (interesting part):
```python
def main():
signal.signal(signal.SIGALRM, handler)
signal.alarm(60)
key = get_random_block()
cipher = BlockCipher(key, 20)
while True:
inp = input("> ")
if inp == 'E':
inp = input("Input (in hex): ")
inp = bytes.fromhex(inp)
assert len(inp) < 90
assert all(b < 243 for b in inp)
if inp == 'gimmeflag':
print("Result: None")
continue
pt = pad(inp)
iv = get_random_block()
enc = iv
for i in range(0, len(pt), 9):
t = add(pt[i:i+9], iv)
iv = cipher.encrypt(t)
enc += iv
print(f"Result: {enc.hex()}")
elif inp == 'D':
inp = input("Input (in hex): ")
inp = bytes.fromhex(inp)
assert len(inp) < 108
assert all(b < 243 for b in inp)
iv, ct = inp[:9], inp[9:]
dec = b''
for i in range(0, len(ct), 9):
t = cipher.decrypt(ct[i:i+9])
dec += sub(t, iv)
iv = ct[i:i+9]
pt = unpad(dec)
if pt == b'gimmeflag':
with open('flag', 'r') as f:
flag = f.read()
print(flag)
exit(0)
elif pt:
print(f"Result: {pt.hex()}")
else:
print("Result: None")
```
So we need to encrypt b'gimmeflag' but the program is supposed to not let us encrypt it.
Somehow we can so I just sent ``b'gimmeflag'.hex() => 67696d6d65666c6167`` to be encrypted and then decrypted
## My ECC Service
Sources :
```python
from Crypto.Util.number import inverse
from hashlib import sha256
import os
import signal
class NonceGenerator:
def __init__(self):
self.state = os.urandom(10)
self.db = {}
def gen(self):
self.state = sha256(self.state + b'wow').digest()[:10]
key = sha256(self.state).digest()[:8]
self.db[key] = self.state
return int.from_bytes(self.state, 'big'), key
def get(self, key: str):
if key not in self.db:
print("Wrong key :(")
exit(0)
return int.from_bytes(self.db[key], 'big')
class ECPoint:
# Classic implem
class MyECCService:
BASE_POINT = (2, 3)
MODS = [
942340315817634793955564145941,
743407728032531787171577862237,
738544131228408810877899501401,
1259364878519558726929217176601,
1008010020840510185943345843979,
1091751292145929362278703826843,
793740294757729426365912710779,
1150777367270126864511515229247,
763179896322263629934390422709,
636578605918784948191113787037,
1026431693628541431558922383259,
1017462942498845298161486906117,
734931478529974629373494426499,
934230128883556339260430101091,
960517171253207745834255748181,
746815232752302425332893938923,
]
def __init__(self):
self.nonce_gen = NonceGenerator()
def get_x(self, nonce: int) -> bytes:
ret = b""
for mod in self.MODS:
p = ECPoint(self.BASE_POINT, mod)
x = (nonce * p).x
ret += x.to_bytes(13, "big")
return ret
def gen(self) -> bytes:
nonce, key = self.nonce_gen.gen()
x = self.get_x(nonce)
return b"\x02\x03" + key + x
def verify(self, inp: bytes) -> bool:
assert len(inp) == 218
nonce = self.nonce_gen.get(inp[2:10])
self.BASE_POINT = (inp[0], inp[1])
x = self.get_x(nonce)
return inp[10:] == x
def handler(_signum, _frame):
print("Time out!")
exit(0)
def main():
signal.signal(signal.SIGALRM, handler)
signal.alarm(300)
service = MyECCService()
for _ in range(100):
service.gen()
while True:
inp = input("> ")
if inp == "G":
payload = service.gen()
print(f"Payload: {payload.hex()}")
elif inp == "V":
payload = bytes.fromhex(input("Payload: "))
result = service.verify(payload)
print(f"Result: {result}")
elif inp == "P":
payload = bytes.fromhex(input("Payload: "))
answer = service.gen()
if payload == answer:
with open("flag.txt", "r") as f:
print(f.read())
else:
print("Wrong :(")
exit(0)
if __name__ == "__main__":
main()
```
The server is computing payloads composed of :
-The coordinates of the base point
-The hash of the current state (I don't know what's this doing here)
-x-coordinate of multiple points of the base point on 16 differents curves
Each time it generates a payload, the state is updated with the hash of itself with the string "wow" appendend. Our goal is to find the next payload so we have to recover the state.
To do that we will use the x-coordinates that are given to us. They represents points that are equal to ``nonce*base_point`` and the nonce is the current state, so we just have to recover the nonce with some discrete log.
Because the order of the base point on some curves has big factors we're gonna do the discrete log on multiple curves with the small factors and then a classic crt.
Solve code (works 1/10 approx, sorry for the server but it flags):
```python
from Crypto.Util.number import inverse
from hashlib import sha256
import os
import signal
from pwn import remote
r = remote("my-ecc-service.chal.perfect.blue",int(1337))
class NonceGenerator:
def __init__(self,state = 0):
self.state = state
self.db = {}
def gen(self):
self.state = sha256(self.state + b'wow').digest()[:10]
key = sha256(self.state).digest()[:8]
self.db[key] = self.state
return int.from_bytes(self.state, 'big'), key
def get(self, key: str):
if key not in self.db:
print("Wrong key :(")
exit(0)
return int.from_bytes(self.db[key], 'big')
class ECPoint:
def __init__(self, point, mod):
self.x = point[0]
self.y = point[1]
self.mod = mod
def inf(self):
return ECPoint((0, 0), self.mod)
def _is_inf(self):
return self.x == 0 and self.y == 0
def __eq__(self, other):
assert self.mod == other.mod
return self.x == other.x and self.y == other.y
def __add__(self, other):
assert self.mod == other.mod
P, Q = self, other
if P._is_inf() and Q._is_inf():
return self.inf()
elif P._is_inf():
return Q
elif Q._is_inf():
return P
if P == Q:
lam = (3 * P.x**2 - 3) * inverse(2 * P.y, self.mod) % self.mod
elif P.x == Q.x:
return self.inf()
else:
lam = (Q.y - P.y) * inverse(Q.x - P.x, self.mod) % self.mod
x = (lam**2 - P.x - Q.x) % self.mod
y = (lam * (P.x - x) - P.y) % self.mod
return ECPoint((x, y), self.mod)
def __rmul__(self, other: int):
base, ret = self, self.inf()
while other > 0:
if other & 1:
ret = ret + base
other >>= 1
base = base + base
return ret
class MyECCService:
BASE_POINT = (2, 3)
MODS = [
942340315817634793955564145941,
743407728032531787171577862237,
738544131228408810877899501401,
1259364878519558726929217176601,
1008010020840510185943345843979,
1091751292145929362278703826843,
793740294757729426365912710779,
1150777367270126864511515229247,
763179896322263629934390422709,
636578605918784948191113787037,
1026431693628541431558922383259,
1017462942498845298161486906117,
734931478529974629373494426499,
934230128883556339260430101091,
960517171253207745834255748181,
746815232752302425332893938923,
]
def __init__(self,state=0):
self.nonce_gen = NonceGenerator(state)
def get_x(self, nonce: int) -> bytes:
ret = b""
for mod in self.MODS:
p = ECPoint(self.BASE_POINT, mod)
x = (nonce * p).x
ret += int(x).to_bytes(13, "big")
return ret
def gen(self) -> bytes:
nonce, key = self.nonce_gen.gen()
x = self.get_x(nonce)
return b"\x02\x03" + key + x
def verify(self, inp: bytes) -> bool:
assert len(inp) == 218
nonce = self.nonce_gen.get(inp[2:10])
self.BASE_POINT = (inp[0], inp[1])
x = self.get_x(nonce)
return inp[10:] == x
MODS = [
942340315817634793955564145941,
743407728032531787171577862237,
738544131228408810877899501401,
1259364878519558726929217176601,
1008010020840510185943345843979,
1091751292145929362278703826843,
793740294757729426365912710779,
1150777367270126864511515229247,
763179896322263629934390422709,
636578605918784948191113787037,
1026431693628541431558922383259,
1017462942498845298161486906117,
734931478529974629373494426499,
934230128883556339260430101091,
960517171253207745834255748181,
746815232752302425332893938923,
]
r.recvuntil(b"> ")
r.sendline(b"G")
r.recvuntil(b": ")
out = bytes.fromhex(r.recvline().rstrip(b"\n").decode())
xs = [Integer(int.from_bytes(out[i:i+13], 'big')) for i in range(10,len(out),13)]
a = -3
x1,y1 = 2,3
subresults = []
factors = []
modulus = Integer(1)
for mod,x in zip(MODS,xs):
print("mod : ",mod)
p = mod
b = (pow(y1, 2, p) - pow(x1, 3, p) - a * x1) % p
E = EllipticCurve(GF(p),[a,b])
G = E(2,3)
P = E.lift_x(Integer(x))
print(P)
order = G.order()
for prime, exponent in factor(order):
_factor = prime ** exponent
if int(_factor).bit_length() >= 30 or int(_factor).bit_length() < 5: continue
print(int(_factor).bit_length())
factors.append(Integer(_factor))
G2 = G*(order//_factor)
A2 = P*(order//_factor)
subresults.append(Integer(discrete_log(A2, G2, operation='+')))
modulus *= _factor
if modulus > 2**81:
break
print("size : ",len(bin(modulus)))
nonce = crt(subresults,factors)
print(nonce)
ngen = MyECCService(int(nonce).to_bytes(10, "big"))
r.recvuntil(b"> ")
r.sendline(b"P")
r.recvuntil(b": ")
r.sendline(ngen.gen().hex())
print(r.recvline())
```