La challenge è una semplice cifratura con RSA in cui però il primo
Indichiamo con
Dato che
Quindi è possibile dedurne che, se
- Prendiamo
$\lfloor\frac{n}{2}\rfloor$ come valore iniziale di$p_0$ - Calcoliamo
$n_0 = p_0\cdot f(p_0)$ - Se
$n_0 < n$ vuol dire che il valore del primo$p$ che stiamo cercando è maggiore di$p_0$ altrimenti che è minore - A questo punto procediamo come nella ricerca binaria
def binary_search(n, n_bits):
a, b = 0, n
p = ((a + b)//2)
while n % p != 0 and p not in [1, n]:
q = generate_second_prime(n_bits, p)
if p*q < n:
a, b = p, b
else:
a, b = a, p
p = ((a + b)//2)
return p, n//p
Un'altra tecnica importante è quella di provare a costruire il primo
che è un'equazione sui primi
Quindi possiamo procedere nel seguente modo per costruire
- Sappiamo che i bit meno significativi di
$p$ e$q$ sono entrambi$1$ , essendo dispari, quindi partiamo con una lista di candidati data da un solo elemento:
candidates = [1]
- Per ogni candidato nella lista, verifichiamo quale possa essere il suo bit successivo. Dato un candidato
c
proviamo a testare come bit successivo sia$1$ che$0$ . Per ciascuno di questi calcoliamo il$q$ corrispondente e verifichiamo se l'equazione$n \mod{2^i} = (p \mod{2^i})\cdot (q \mod{2^i})$ è valida. Se lo è aggiungiamo il nuovo candidato alla lista, altrimenti lo scartiamo.
def search_with_candidates(n, n_bits):
candidates = [1]
for i in range(1, n_bits):
new_candidates = []
for c in candidates:
for b in [0,1]:
possible_p = c + b*2**i
possible_q = generate_second_prime(i+1, possible_p)
if ((possible_p*possible_q) % 2**(i+1)) == (n % 2**(i+1)):
new_candidates.append(possible_p)
candidates = new_candidates[:]
for c in candidates:
if n%c == 0:
return c, n//c
raise Exception("Prime not found")
from Crypto.Util.number import isPrime, long_to_bytes
e = 65537
n = 3637400592141233053318303969046830876095580140855488833015304207130621295563617144193449778130146429235997567238720206838185374871913987902223981111694348323418739467704699968504263728458229055653571394452433642631694996588693092104798191817534563009299334375307873871479302638441206077852699432268647990793663566966521773196973270814465617055277014656580584957078884192564762129155432028080595364741266629162254969520149534387434381244067539296823332241573408517050613560436344530372143239028799557682567433850259477291205708952194250569729667458741243914742703705791789934874751084544389695970001574563357092617545184676320826820159122325980498989599327069001659755319055027479902935187549483616076942681091178586529367283446330939129905925913286207341103644775278407859109072901754487888245126284059167729062259953149500477613275922923526130302966024792147916266067202117122767618564413984607610874376274884601265902009593
c = 1655355477614725612326422801205654877153080140934683428882259793084639106115888344087475378116701397163573359080667070466301581841165682635444225467621376879028265845200417675411957733990127166864545252527726341439173387141659284443969047753331664302512055816466043523216757941892216028198045457204121696059880460850993225208201818563800454385936127215140806793291869684379413641376098990886209220526244581344109561158979268164451880821578009427297389894607793649073020633897931021875052533975748762718599063074637040985914014817824594781556829792222319250544787513048629033230346849193900562970003140272836934559676170690791681457233204121884138289007560796468623852258130364150530365533423573124461481907646394604342186444873177972242783039921686397715848501012679703895403159284449510822928617975181794895132833879302286751191844491913951297282845352765885767209341868827516990441630639813899722532299502152935651006231333
n_bits = 1024
def generate_second_prime(n_bits, p):
p_bits = (bin(p)[2:]).zfill(n_bits)
increased_bits = [int(b) + 2 for b in p_bits]
q = sum([int(d) * int(4**(n_bits - 1 - i)) for i,d in enumerate(increased_bits)])
return q
def binary_search(n, n_bits):
a, b = 0, n
p = ((a + b)//2)
while n % p != 0 and p not in [1, n]:
q = generate_second_prime(n_bits, p)
if p*q < n:
a, b = p, b
else:
a, b = a, p
p = ((a + b)//2)
return p, n//p
def search_with_candidates(n, n_bits):
candidates = [1]
for i in range(1, n_bits):
new_candidates = []
for c in candidates:
for b in [0,1]:
possible_p = c + b*2**i
possible_q = generate_second_prime(i+1, possible_p)
if ((possible_p*possible_q) % 2**(i+1)) == (n % 2**(i+1)):
new_candidates.append(possible_p)
candidates = new_candidates[:]
for c in candidates:
if n%c == 0:
return c, n//c
raise Exception("Prime not found")
# p, q = search_with_candidates(n, n_bits)
p, q = binary_search(n, n_bits)
assert p*q == n
phi = (p-1)*(q-1)
d = pow(e, -1, phi)
flag = long_to_bytes(pow(c, d, n)).decode()
print(flag)