###ENG PL
We are given following python code - it is supposed to sign messages:
#!/usr/bin/python
from hashlib import *
from Crypto.Util.number import *
from gmpy import *
import os
def param_gen():
q = getPrime(1024)
while True:
t = 2*getRandomRange(1, 2**256)
p = t*q + 1
if is_prime(p):
break
while True:
h = getRandomRange(1, p-1)
g = pow(h, (p-1)/q, p)
if g != 1:
break
return (p, q, g)
def key_gen(params):
p, q, g = params
x = getRandomRange(1, q)
y = pow(g, x, p)
pubkey, privkey = y, x
return pubkey, privkey
def sign(msg, privkey, params):
p, q, g = params
while True:
k = getRandomRange(1, 1024) * pow(pubkey, privkey, p) % q
r = pow(g, k, p) % q
s = invert(k, q) * ( int(sha512(msg).hexdigest(), 16) + privkey * r) % q
if r*s != 0 :
break
return (int(r), int(s))
def verify(msg, signature, pubkey, params):
p, q, g = params
r, s = signature
if (0 < r < q) and (0 < s < q) :
w = invert(s, q)
u1 = (int(sha512(msg).hexdigest(), 16) * w) % q
u2 = (r * w) % q
v = ((pow(g, u1, p) * pow(pubkey, u2, p)) % p) % q
if v == r:
return True
return False
params = param_gen()
p, q, g = params
print params
pubkey, privkey = key_gen(params)
for i in range(0, 40):
msg = 'ASIS{' + os.urandom(16).encode('hex') + '}'
signature = sign(msg, privkey, params)
r, s = signature
print str((msg, r, s))
This challenge asks us to recover private key (x) given only a handful (around 40) signatures, public key, and public parameters given (attached to this solution in github).
After a while, we find a vulnerability in this challenge -
k = getRandomRange(1, 1024) * pow(pubkey, privkey, p) % q
This is obviously insecure, as only 1024 ks can possibly be generated. If only we could recover k, this challenge would be trivial - given k we can calculate privkey from linear equation:
s = invert(k, q) * ( int(sha512(msg).hexdigest(), 16) + privkey * r) % q
<=>
s * k - int(sha512(msg).hexdigest(), 16) * invert(r, q) % q = privkey
But we don't know k... yet.
After another while, we noticed that if we could find two messages encrypted with the same k, challenge would be easy again. We could solve system of linear equations:
s = invert(k, q) * ( int(sha512(msg0).hexdigest(), 16) + privkey * r) % q
s = invert(k, q) * ( int(sha512(msg1).hexdigest(), 16) + privkey * r) % q
...where k and privkey are unknowns. But again, this wasn't the case in this challenge.
And finally, when we began to fall into the pit od despair, we noticed that we could create another system of linear equations. Because k = (1..1024) * MAGIC (where MAGIC = pow(publickey, privkey)
, but it's not important), we can write above equations as:
s = invert(k1 * MAGIC, q) * (int(sha512(msg0).hexdigest(), 16) + privkey * r) % q
s = invert(k2 * MAGIC, q) * (int(sha512(msg1).hexdigest(), 16) + privkey * r) % q
In more mathematical terms, we can solve this system of equations as follows:
s1 = (c1 + priv*r1) / (k1*magic)
s2 = (c2 + priv*r2) / (k2*magic)
1 = (c1 + priv*r1) / (k1*magic*s1)
1 = (c2 + priv*r2) / (k2*magic*s2)
(c1 + priv*r1) / (k1*magic*s1) = (c2 + priv*r2) / (k2*magic*s2)
(c1 + priv*r1) * (k2*magic*s2) = (c2 + priv*r2) * (k1*magic*s1)
c1*k2*magic*s2 + priv*r1*k2*magic*s2 = c2*k1*magic*s1 + priv*r2*k1*magic*s1
c1*k2*s2 + priv*r1*k2*s2 = c2*k1*s1 + priv*r2*k1*s1
priv*r1*k2*s2 - priv*r2*k1*s1 = c2*k1*s1 - c1*k2*s2
priv*(r1*k2*s2 - r2*k1*s1) = c2*k1*s1 - c1*k2*s2
priv = (c2*k1*s1 - c1*k2*s2) / (r1*k2*s2 - r2*k1*s1)
Great, we have private key! But we have to bruteforce 1024 possible values of k1 and k2. It's easy enough in python:
from hashlib import *
from Crypto.Util.number import *
from gmpy import *
import os
def hash(msg):
return int(sha512(msg).hexdigest(), 16)
def solve_for(params, set1, set2):
p, q, g = params
msg1, r1, s1 = set1
msg2, r2, s2 = set2
c1, c2 = hash(msg1), hash(msg2)
for k1 in range(1, 1024):
print k1
for k2 in range(1, 1024):
priv = (c2*k1*s1 - c1*k2*s2) * invert(r1*k2*s2 - r2*k1*s1, q) % q
if pubkey == pow(g, priv, p):
print priv
params = (
19990043472646209601994864878783430356973105946195950979159251182377121897576105462833887676561348770957108482823143337701724893247634876231133001112659622843245186144815694000013210989382541478073551247763586093331215996260234193847013210807939190828438263706901950228866893621933887960930392778176297205331569773161563794018138992335091883899396920586572927792555042350138728812073331L, 91629484598379105033512409529628860433949558415030791938154302542936417405614272511539966765257644706180090102931421315331051281240103609205686784098900471134711603588304765157414857991689054932897002635007537146809343047302801243835776634361209432398032301656027511721047704054332653738042312201931970395721L, 13622145302845273763875935516952425125176393702394844695432724066597834898277677169908234619701079219174550050531086255052810547971127992364106395259623997686454799745423099095097056224348731737512112568608406081844751809559052204755863089238863185755017905098158596830866362329243334855589147537151782745634416243769180780246626300677625340613515232605024658651528787608437872606367959L)
set1 = ('ASIS{f178fcdecf80695744078436c8443d21}', 90237121958952251064976624492762150213417521896687572941345633764310618911601030617031373795520024583293535375710106225552196376797438589929204291257545448726331743603280815369427093966770848920593554708454565838054224752549404158493278464435213107237527453340811242850765181020853492694252650516970176641788L, 45699965566033911695982803604354032305058506238185457115666900773211243249439993822400900118887663777494151014067272471514622506941212402877792085312967213090334714190020080393819021065260516916603206309212756987461405393121419856054170230373811099272880018713486783939408418428725471847346917597357388173871L)
set2 = ('ASIS{13d82a52a86b3f60d1d5d0a9a8fbd38c}', 82549501691566010240713957921730895287132128726058380260123211081436703044836270709304732846086147549660502736557189123678902405383474467404416541210982435469292602012427497998459504126706153698217704534579606635614323510632001857553260216879546038347723055837869594047336385462982125121614977106061534077159L, 14100015776662279808055950447194278668824709578034279558537621662053558677298084425976090376958899238099311275299915689425374231782256233762239739696534102757257328540054634833325260578944419955606514868448883176935887227050496260234118031035188783965371809668020726016057511098782430965590465392354859239207L)
pubkey = 5120206348411789470161536127663499609309512038887109068976130122757582186109731229884381538683899917243667766639035225751229538595906667027298668257572271684995053081914448593696545954848592131805237134557157103971058735552127997038702518466039596176245969237004179261427946425137677159565508198606851760199742107673019486271187331752618149416170682584515782866631954825392784384052948
solve_for(params, set1, set2)
And we finally get our flag: ASIS{1e58445616cd5178632ae15bef51c4a3}
###PL version
to be done?