ångstromCTF 2019 | EightBall

#angstromCTF2019

https://ctftime.org/task/8346

Our remote eightball has state of the art encryption protecting the contents of any question asked to it.

nc 54.159.113.26 19000

from gmpy2 import div, gcd, invert, powmod

from Crypto.Util.number import getPrime, getRandomRange

def unpack(fn):
    with open(fn, 'r') as f:
        return tuple([int(x) for x in f.readlines()])

def pack(fn, k):
    with open(fn, 'w') as f:
        f.write('\n'.join([str(x) for x in k]))

def generate():
    r = 257
    while True:
        p = getPrime(1024)
        if (p-1) % r == 0 and gcd(r, div(p-1, r)) == 1:
            break
    while True:
        q = getPrime(1024)
        if gcd(r, q-1) == 1:
            break
    n = p*q
    phi = (p-1)*(q-1)
    while True:
        y = getRandomRange(0, n)
        x = powmod(y, phi*invert(r, n) % n, n)
        if x != 1:
            break
    return (n, y), (n, phi, x)

def encrypt(m, pk):
    r = 257
    n, y = pk
    u = getRandomRange(0, n)
    return powmod(y, m, n)*powmod(u, r, n) % n

def decrypt(c, sk):
    r = 257
    n, phi, x = sk
    a = powmod(c, phi*invert(r, n) % n, n)
    for i in range(256):
        if powmod(x, i, n) == a:
            return i
    return 0
import binascii
import socketserver

from Crypto.Random.random import choice
from Crypto.Util.asn1 import DerSequence

import benaloh

answers = [
        b'It is certain',
        b'It is decidedly so',
        b'Without a doubt',
        b'Yes definitely',
        b'You may rely on it',
        b'As I see it, yes',
        b'Most likely',
        b'Outlook good',
        b'Yes',
        b'Signs point to yes',
        b'Reply hazy try again',
        b'Ask again later',
        b'Better not tell you now',
        b'Cannot predict now',
        b'Concentrate and ask again',
        b'Don\'t count on it',
        b'My reply is no',
        b'My sources say no',
        b'Outlook not so good',
        b'Very doubtful'
    ]

sk = benaloh.unpack('sk')

def handle(self):
    while True:
        der = DerSequence()
        der.decode(binascii.unhexlify(self.query(b'Question: ')))
        question = bytes([benaloh.decrypt(c, sk) for c in der])
        response = choice(answers)
        self.write(response)

class RequestHandler(socketserver.BaseRequestHandler):

    handle = handle

    def read(self, until=b'\n'):
        out = b''
        while not out.endswith(until):
            out += self.request.recv(1)
        return out[:-len(until)]

    def query(self, string=b''):
        self.write(string, newline=False)
        return self.read()

    def write(self, string, newline=True):
        self.request.sendall(string)
        if newline:
            self.request.sendall(b'\n')

class Server(socketserver.ForkingTCPServer):

    allow_reuse_address = True

    def handle_error(self, request, client_address):
        pass

port = 3000
server = Server(('0.0.0.0', port), RequestHandler)
server.serve_forever()
Question: 30825140028201002204948fa6880bb3395282f978668975c0819a434ebfb400a990396d062c0c4d552398d5262...

暗号化の感じはPaillier暗号っぽいけど復号はなんだろう。暗号化も N^2じゃなくて Nの下でやっていて不穏。