Skip to content

Latest commit

 

History

History
166 lines (114 loc) · 6.14 KB

README.md

File metadata and controls

166 lines (114 loc) · 6.14 KB

mDLP

Description

mDLP states the Discrete Logarithm Problem in matrices! Download and break it!

mdlp.sage:

#!/usr/bin/env sage

from sage.all import *
from Crypto.Util.number import *
from secret import gen_prime, gen_base_matrix, flag

def keygen(nbit, l):
	#
	## Create the n-bit prime and base square matrix of size l over Ring Z_p
	#
	p = gen_prime(nbit)
	A = gen_base_matrix(p, l)

	d = randint(2, p)
	Q = A ** d
	pubkey = (p, A, Q)
	privkey = d
	return pubkey, privkey

def encrypt(M, pubkey):
	p, A, Q = pubkey
	l = A.nrows()
	assert M.nrows() == l
	r = randint(2, p)
	C, D = A ** r, Q ** r
	E = D * M
	return C, E

def msg_to_matrix(p, msg):
	l = len(msg)
	return matrix(Zmod(p), [[bytes_to_long(msg[0:l//4]), bytes_to_long(msg[l//4:l//2])], 
			[bytes_to_long(msg[l//2:3*l//4]), bytes_to_long(msg[3*l//4:l])]])

nbit, l = 256, 2
pubkey, privkey = keygen(nbit, l)
p, A, Q = pubkey
M = msg_to_matrix(p, flag)
ENC = encrypt(M, pubkey)

print(f'pubkey = {pubkey}')
print(f'ENC = {ENC}')

output.txt:

pubkey = (94413310751917369305079812653655619566021075449954923397392050181976939189891, [38199337272663519912859864066101528484023656231849338967558894235177040565160 39708167173513090810083500967474691943646789486489990958101592717502452906918]
[ 8216211510625558273096642057121313417997488994504871245106775381995665925783 56213973479253849392219948587554091081997419218105584429833155946799898624740], [61709241598677561125021718690991651934557899286972116933820920757636771220273  1945367449329759288724720626216309893787847192907494307536759223359193510642]
[37495232301500074672571996664644922614693962264764098174213150757616575323566  7348269231944161963123250652171976847627786311806728904368575861561449853500])
ENC = ([47566868540912475779105819546118874217903268597017385039977593878486632022506 86073162301954995219912739344010659248720823814557810528618517154406350653517]
[23443866424088482893369441934221448179896589659663581973508963775891809430857 74567333640177484678138574534395714128854315440076840728428649074147859070975], [56937964695156855099385034285428853461603799261684034842341841781057485084327 82459328835322885824854425864023809222717401981993182346342472865578156162544]
[85092677346274708324092648597361729755305119435989183201786866456596369562681 22228861714953585357281182780002271505668586948202416054453861940155538803489])

Writeup

There are some utilities defined in functions.py file. Each function is described in its docstring.

Then we diagonalize matrix A and find out its eigenvalues are 10 and -1. 10 is a primitive root modulo p. Also, p-1's prime factors are all below 1000, so we can multiply P and P^-1 to A^d and A^r and then solve the DLP problem on the resulting matrices using CRT and number-theoretic stuff. After finding r and d, we calculate r.d and then calculate A**(r.d)**-1. It's evident that by calculating the product of this matrix and C(ciphertext matrix), we can find P(plaintext matrix).

In [1]: exec(open('functions.py', 'r').read())

In [2]: A = [[38199337272663519912859864066101528484023656231849338967558894235177040565160 , 39708167173513090810083500967474691943646789486489990958
   ...: 101592717502452906918],
   ...: [ 8216211510625558273096642057121313417997488994504871245106775381995665925783 , 5621397347925384939221994858755409108199741921810558442983315
   ...: 5946799898624740]]

In [3]: ev = eigenvals(A)

In [4]: P = Pof(A, ev)

In [5]: Q = inv(P)

Import functions, define matrix A, calculate its eigenvalues, P and Q!

In [6]: Ad = [[61709241598677561125021718690991651934557899286972116933820920757636771220273 , 1945367449329759288724720626216309893787847192907494307
   ...: 536759223359193510642] ,
   ...: [37495232301500074672571996664644922614693962264764098174213150757616575323566 , 7348269231944161963123250652171976847627786311806728904368575
   ...: 861561449853500]]

In [7]: Ar = [[47566868540912475779105819546118874217903268597017385039977593878486632022506 , 8607316230195499521991273934401065924872082381455781052
   ...: 8618517154406350653517],
   ...: [23443866424088482893369441934221448179896589659663581973508963775891809430857 , 7456733364017748467813857453439571412885431544007684072842864
   ...: 9074147859070975]]

In [8]: T = mult(Q, mult(Ad, P))

In [9]: T
Out[9]:
[[69057510830621723088144969343163628782185685598778845838189496619198221073772,
  0],
 [0, 1]]

Then define A**d and A**r. Q.A**d.P is a diagonal matrix as expected.

In [10]: factors = [2, 5, 103, 131, 139, 149, 181, 223, 263, 313, 337, 347, 349, 359, 389, 409, 479, 509, 547, 599, 613, 643, 751, 757, 773, 787, 6352
    ...: 09, 839, 857, 877]

In [16]: T = mult(Q, mult(Ar, P))

In [17]: rems = list(map(lambda x: find_r(T[0][0], x), factors))

In [19]: _, r = crt(factors, rems)

In [20]: T = mult(Q, mult(Ad, P))

In [21]: rems = list(map(lambda x: find_r(T[0][0], x), factors))

In [22]: _, d = crt(factors, rems)

In [23]: pow(10, d, p) == T[0][0]
Out[23]: True

Factorize p-1 and find r mod factor for each of those factors. These factors are not necessarily prime, but they're coprime.

After finding these remainders, we use CRT to combine them and find r and d modulo p-1.

In [24]: dr = d * r % (p - 1)

In [25]: Adr = mult(P, mult([[pow(10, dr, p), 0],[0, pow(-1, dr, p)]], Q))

In [26]: C = [[56937964695156855099385034285428853461603799261684034842341841781057485084327 , 8245932883532288582485442586402380922271740198199318234
    ...: 6342472865578156162544],
    ...: [85092677346274708324092648597361729755305119435989183201786866456596369562681 , 222288617149535853572811827800022715056685869482024160544538
    ...: 61940155538803489]]

In [27]: P = mult(inv(Adr), C)

Then calculate inverse of A**dr and decrypt C to earn P

In [28]: from Crypto.Util.number import bytes_to_long, long_to_bytes

In [29]: long_to_bytes(P[0][0]) + long_to_bytes(P[0][1]) + long_to_bytes(P[1][0]) + long_to_bytes(P[1][1])
Out[29]: b'ASIS{PuBl1c-K3y_CRyp70sy5tEm_B4S3d_On_Tw0D!m3nSiOn_DLP!}'

Use long_to_bytes to find the flag at last.