Tokyo Westerns CTF 3rd 2017: BabyPinhole
- こういう話があった: https://twitter.com/elliptic_shiho/status/907221511173312513
- 加法のそれを使ってshiftしていい感じにしてるだけだからPaillier暗号である必然性はないよね、ぐらいの気持ち
- この手のアレはHardcore Bitとか言うそうな
problem
Paillier暗号による公開鍵(n,g)と暗号文cが与えられている。 整数bが固定されており、復号結果のb-bit目を返すoracleがある。 cを復号せよ。
solution
準同型性(加法)をやる。
Paillier暗号は準同型性を持つ: enc(m1;r1)enc(m2;r2)=enc(m1+m2;r1r2)。 oracle(c⋅enc(δ;r2))とすればc+δの復号結果のb-bit目が得られる。 δ=2b−1としてoracle(c)≠oracle(c⋅enc(2b−1;r2))であればm+2b−1としたときに繰り上がりが発生していたことになり、つまりmのb−1-bit目は1であるかどうかが分かる。 繰り上がりが発生したかどうかでb-bit目が変わるようにδを決めて繰り返していけばflagが取れる。 flagの後半については単純にすればよい。flagの前半についてはmodのoverflowが効いてくるので少し手間だが、やればなんとかなる。
implementation
#!/usr/bin/env python2
from pwn import * # https://pypi.python.org/pypi/pwntools
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('host', nargs='?', default='ppc2.chal.ctf.westerns.tokyo')
parser.add_argument('port', nargs='?', default=38264, type=int)
parser.add_argument('--log-level', default='debug')
args = parser.parse_args()
context.log_level = args.log_level
# params
from Crypto.Util.number import *
from hashlib import sha1
bits = 1024
def LCM(x, y):
return x * y // GCD(x, y)
def L(x, n):
return (x - 1) // n
def encrypt(m, r):
m %= n2
assert 1 <= r < n2
c = pow(g, m, n2) * pow(r, n, n2) % n2
return c
def complement(s):
return ''.join(map(lambda c: str(1 - int(c)), s))
with open('publickey') as fh:
n = int(fh.readline(), 16)
n2 = int(fh.readline(), 16)
g = int(fh.readline(), 16)
with open('ciphertext') as fh:
ciphertext = int(fh.readline(), 16)
# connect
p = remote(args.host, args.port)
def oracle(c, memo={}):
c %= n2
assert 0 <= c < n2
if c not in memo:
p.sendline(hex(c))
memo[c] = bool(int(p.recvline()))
return memo[c]
# find b
i = 512 + 3
while oracle(ciphertext) == oracle(ciphertext * encrypt(1 << i, 1) % n2):
i -= 1
b = i
log.info('b = %d', b)
# offset
oracle_ciphertext = oracle(ciphertext)
if oracle_ciphertext:
ciphertext *= encrypt(- 1 << b, 1)
# mc
i = 1023
mc = ''
while i >= 0:
pad = (int(complement(mc) + '1', 2) << i)
bit = (int(oracle(ciphertext) != oracle(ciphertext * encrypt(pad, 1) % n2)))
mc += str(bit)
log.info('i = %d: mc = %s', i, mc)
i -= 1
# m
m = (- (int(complement(mc), 2) + 1 - (1 << b))) % n
if oracle_ciphertext:
m += 1 << b
log.info('m = %d', m)
# flag
oracle(ciphertext * encrypt(- m, 1), memo={})
flag = 'TWCTF{' + sha1(str(m).encode('ascii')).hexdigest() + '}'
log.info('flag = %s', flag)
- 2017年 9月 11日 月曜日 21:50:55 JST
- 怪しい部分を修正