''' e = 65537 N = 3333577291839009732612693330613476891341287017491683764014849337158389717338712200133085615150269196268856288361865352673921704626130772582853528604556994221890454520933132803888321775335519781063447756692130742361931522856942232406992357982482263472763363458621836220024977864980600979194500121897419553619426163227 c = 1277272201928931051067525742142583320131498687502905469530557519241347169899260720694873154669476372724906606385788056536109971768256973988460766527896895880291037980646963981472637862512247195798266373251524526460097881602691641026093728861572872156172787168597410496150253340538386296663073088345799201197096884740 k = 9352039867057736323 r1 = 10421792656200324147964684790160875926436411483496860422433732508593789212449544620816674407170998779863336939494663076247759140488927744939619406024905901 r2 = 8806088830734144089522276896226392806947836111998696180055727048752624989402057411311728398322297424598954586424896296000606209022432442660527640463521679 leak1 = 4266222222502644630611545246271868348722888987303187402827005454059765428769160822475080050046035916876078546634293907218937483241284454918367519709206766322037148585465519188582916280829212776096606923824120883699251868362915920299645 leak2 = 1176921186497191878459783787148403806360469809421921990427675048480656171919274113895695842508460760829511824635106692634456334400022597605585661597793889066395539405395254174368285751236344600489419240628821864912762242188289636510706 '''
前半部分可以直接爆破得到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
from Crypto.Util.number import * import itertools N = 3333577291839009732612693330613476891341287017491683764014849337158389717338712200133085615150269196268856288361865352673921704626130772582853528604556994221890454520933132803888321775335519781063447756692130742361931522856942232406992357982482263472763363458621836220024977864980600979194500121897419553619426163227 e = 65537 c = 1277272201928931051067525742142583320131498687502905469530557519241347169899260720694873154669476372724906606385788056536109971768256973988460766527896895880291037980646963981472637862512247195798266373251524526460097881602691641026093728861572872156172787168597410496150253340538386296663073088345799201197096884740
# get p for i inrange(2, 2**25): if N % i == 0: q = i break
p = N // q
# d = inverse(e, (p-1)*(q-1)) # print(long_to_bytes(pow(c,d,N))) # b'Hurry up and go smelt copper!'
defsmall_roots(f, bounds, m=6, d=None): ifnot d: d = f.degree()
R = f.base_ring() N = R.cardinality() #取得模数
f /= f.coefficients().pop(0) #最高次项系数化为0,coefficients是多项式的降次幂排列系数 f = f.change_ring(ZZ)
G = Sequence([], f.parent())
for i inrange(m + 1): base = N ** (m - i) * f ** i #收集基多项式
for shifts in itertools.product(range(d), repeat=f.nvariables()): g = base * prod(map(power, f.variables(), shifts)) G.append(g) # print(G) B, monomials = G.coefficient_matrix() monomials = vector(monomials)
factors = [monomial(*bounds) for monomial in monomials] for i, factor inenumerate(factors): B.rescale_col(i, factor)
B = B.dense_matrix().LLL()
B = B.change_ring(QQ) for i, factor inenumerate(factors): B.rescale_col(i, 1 / factor)
H = Sequence([], f.parent().change_ring(QQ)) for h infilter(None, B * monomials): H.append(h) I = H.ideal() if I.dimension() == -1: H.pop() elif I.dimension() == 0: roots = [] for root in I.variety(ring=ZZ): root = tuple(R(root[var]) for var in f.variables()) roots.append(root) return roots return []
R = PolynomialRing(Zmod(p), names=('x', 'y')) inv_k = inverse_mod(k, p) x, y = R.gens()
from Crypto.Util.number import * import random from secret import flag, key
p, q, e, d = key n = p * q
assert isPrime(p) and isPrime(q) and p.bit_length() == 512and q.bit_length() == 512 print('d_len:', d.bit_length()) # d_len: 500
classQN: def__init__(self, a, b, c, d, n): self.a = a % n self.b = b % n self.c = c % n self.d = d % n self.n = n def__mul__(self, other): ifisinstance(other, QN) andself.n == other.n: n = self.n a1, b1, c1, d1 = self.a, self.b, self.c, self.d a2, b2, c2, d2 = other.a, other.b, other.c, other.d a = (a1*a2 - b1*b2 - c1*c2 - d1*d2) % n b = (a1*b2 + b1*a2 + c1*d2 - d1*c2) % n c = (a1*c2 - b1*d2 + c1*a2 + d1*b2) % n d = (a1*d2 + b1*c2 - c1*b2 + d1*a2) % n return QN(a, b, c, d, n) returnNotImplemented def__pow__(self, exp): if exp <= 0: return QN(1, 0, 0, 0, self.n) result = QN(1, 0, 0, 0, self.n) base = self while exp > 0: if exp & 1: result = result * base base = base * base exp >>= 1 return result def__repr__(self): returnf"({self.a}, {self.b}, {self.c}, {self.d})" def__eq__(self, other): return (self.a == other.a andself.b == other.b and self.c == other.c andself.d == other.d andself.n == other.n)
if __name__ == "__main__": m = QN(bytes_to_long(flag), random.randint(1,n-1), random.randint(1,n-1), random.randint(1,n-1), n) c = pow(m, e) assertpow(c,d) == m
''' n = 85481717157593593434025329804251284752138281740610011731799389557859119300838454555657179864017815910265870318909961454026714464920305413622061116245330661303912116693461205161551044610609272231860357133575507519403908786715597649351821576114881230052647979679534076432015415470679178775688932706964062378627 e = 622349328830189017262721806176220642327451718814004869262654184548169579851269489422592218838968239824917128227573062775020729663341881800222644869706115998147909113383905386637703321110321003518025501597602036772247509043126119242571435842445265921450671551669304835480011469949693693324643919337459251944818821206437044742271947245399811180478630764346756372873090874700249814285609571282905316777766489385036566372369518133091334281269104669836052038324087775082397535339943512028851288569342237442241378961242047171826362264504999955091800815867645003788806864324904993634075730184915611726197403247247938385732000097424282851846018331719216174462481994636142469669316961566262677169345291992925101965060785779535371861314213957527417556275049382603735394888681049143483994633920712406197215676594926797093225468201559158552767178665382859062516627874818691572997614241454801824762125841557409876879638813879540588189811 c = (36509962693210047517809190780500733945629638467721636016118307831299153205787169088399018032858962653944360359037757238416729623515314461908869670066385367461579954207170900898502608201371741903312247217007567631584237670049543882850246347784852813361080564895289678219739976819925055830837232548960336550804, 14959247128290207711158598578966149380261887381574636597156641284189267790471920774170808806288580563577492441070024491886953389517733477847472737986545246252874395600374486543947605977380365673302757291495953658030048738906460472042379676160137626447499571382731894905380992263233204548600668812780247601325, 36653805985529315558503796353782648503316310086826701482263862429608379730584363732938416744191295088641419179725673205148217999183797829423539295825286947419128575063946728227807922575922697370871241826105471260524875137135999213015948866472957081351066130709476717779611974377854714476824268335455979590736, 44619982799889884704010277482810139576960205880619960462167175653326841572868809642692412859814472796539211092403704130039198480671655784971458045667408446084843398171460450068014922244839889367385992492875980531522963147513445040259751323986442839404788429909271285196520486381047903450020895598546088952188) '''
def__pow__(self, exp): result = QN(1, 0, 0, 0, self.n) base = self while exp > 0: if exp & 1: result = result * base base = base * base exp >>= 1 return result
n = 85481717157593593434025329804251284752138281740610011731799389557859119300838454555657179864017815910265870318909961454026714464920305413622061116245330661303912116693461205161551044610609272231860357133575507519403908786715597649351821576114881230052647979679534076432015415470679178775688932706964062378627 e = 622349328830189017262721806176220642327451718814004869262654184548169579851269489422592218838968239824917128227573062775020729663341881800222644869706115998147909113383905386637703321110321003518025501597602036772247509043126119242571435842445265921450671551669304835480011469949693693324643919337459251944818821206437044742271947245399811180478630764346756372873090874700249814285609571282905316777766489385036566372369518133091334281269104669836052038324087775082397535339943512028851288569342237442241378961242047171826362264504999955091800815867645003788806864324904993634075730184915611726197403247247938385732000097424282851846018331719216174462481994636142469669316961566262677169345291992925101965060785779535371861314213957527417556275049382603735394888681049143483994633920712406197215676594926797093225468201559158552767178665382859062516627874818691572997614241454801824762125841557409876879638813879540588189811 c = QN( 36509962693210047517809190780500733945629638467721636016118307831299153205787169088399018032858962653944360359037757238416729623515314461908869670066385367461579954207170900898502608201371741903312247217007567631584237670049543882850246347784852813361080564895289678219739976819925055830837232548960336550804, 14959247128290207711158598578966149380261887381574636597156641284189267790471920774170808806288580563577492441070024491886953389517733477847472737986545246252874395600374486543947605977380365673302757291495953658030048738906460472042379676160137626447499571382731894905380992263233204548600668812780247601325, 36653805985529315558503796353782648503316310086826701482263862429608379730584363732938416744191295088641419179725673205148217999183797829423539295825286947419128575063946728227807922575922697370871241826105471260524875137135999213015948866472957081351066130709476717779611974377854714476824268335455979590736, 44619982799889884704010277482810139576960205880619960462167175653326841572868809642692412859814472796539211092403704130039198480671655784971458045667408446084843398171460450068014922244839889367385992492875980531522963147513445040259751323986442839404788429909271285196520486381047903450020895598546088952188, n, ) print(e.bit_length())
defwienerAttack(N, e, c): res = [] rres = [] cf = continued_fraction(e / N) convers = cf.convergents() for pkds in convers: pk, pds = pkds.as_integer_ratio() if pk == 0or pds == 0: continue if (e * pds - 1) % pk != 0: continue m = c ** pds mes = long_to_bytes(m.a) if mes.endswith(b"}"): print(pds.bit_length()) return mes
res = wienerAttack(n, e, c) assert res if res: print(res) # 500 # b'flag{Qu4t3rNion_l5_S0_6rea7_&_Ch4rm1n9}'
from sage.allimport * from sage.crypto.util import random_blum_prime from Crypto.Util.number import * from secret import flag
nbit = 512 gamma = 0.44 delta = 0.51 dm,dl = 0.103, 0.145 cpbit = ceil(nbit * gamma) kbit = int(nbit * delta) msbit = int(nbit * dm) lsbit = int(nbit * dl) g = random_blum_prime(2**(cpbit - 1), 2**cpbit-1) while1: p = q = 0 while is_prime(p) orlen(bin(p)) - 2 != nbit // 2: a = randint(int(2 ** (nbit // 2 - 2) // g * gamma), 2 ** (nbit // 2 - 1) // g) p = 2 * g * a + 1 while is_prime(q) orlen(bin(q)) - 2 != nbit // 2: b = randint(int(2 ** (nbit // 2 - 2) // g * gamma), 2 ** (nbit // 2 - 1) // g) q = 2 * g * b + 1 L = 2 * g * a * b if is_prime(L + a + b): n = p * q break
d = random_prime(2**kbit-1, lbound=2**(kbit - 1)) e = inverse_mod(d, L) k = (e * d - 1) // L dm = d // (2 ** (kbit - msbit)) dl = d % (2 ** lsbit) m = bytes_to_long(flag) print(dm, dl, e, n) print(pow(m, e, n)) """ 3203202584971257 7274383203268085152331 36346110007425305872660997908648011390452485009167380402907988449045651435844811625907 8073736467273664280056643912209398524942152147328656910931152412352288220476046078152045937002526657533942284160476452038914249779936821603053211888330755 8042279705649954745962644909235780183674555369775538455015331686608683922326562829164835918982642084136603628007677118144681339970688028985720674063973679 """
from collections import Counter from Crypto.Util.number import * e = 36346110007425305872660997908648011390452485009167380402907988449045651435844811625907 n = 8073736467273664280056643912209398524942152147328656910931152412352288220476046078152045937002526657533942284160476452038914249779936821603053211888330755 c = 8042279705649954745962644909235780183674555369775538455015331686608683922326562829164835918982642084136603628007677118144681339970688028985720674063973679
defroots_mod_prime_power(p, k, e, c): mod = p ** k if c % mod == 0: t = (k + e - 1) // e base = p ** t return [base * r for r inrange(p ** (k - t))] if k == 1: return [power_mod(c % p, inverse_mod(e, p - 1), p)] # For p odd, gcd(e, phi(p^k)) = 1 gives a unique root. root_p = power_mod(c % p, inverse_mod(e, p - 1), p) return [crt([root_p], [p], [c], [p ** k])[0] % mod]
prime_powers = Counter(factors) mods_roots = [] for p, k in prime_powers.items(): phi = (p - 1) * (p ** (k - 1)) assert gcd(e, phi) == 1 roots = roots_mod_prime_power(p, k, e, c) mods_roots.append((p ** k, roots))
solutions = [(0, 1)] for mod, roots in mods_roots: new_solutions = [] for a, m in solutions: for r in roots: x = crt([a, r], [m, mod]) new_solutions.append((x, m * mod)) solutions = new_solutions
hits = 0 for m, mod in solutions: if mod != n: continue pt = long_to_bytes(m) if pt.endswith(b"}"): hits += 1 print("hit:", pt)
print("total hits:", hits)
''' hit: b'flag{baby_so_rsa_this_is}' total hits: 1 '''
沒有那個n,我照樣可以解出flag。 Even without that n, I can still
solve the flag.
loss.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
from Crypto.Util.number import * from gmpy2 import * from secret import flag
m = bytes_to_long(flag) p = getPrime(512) q = next_prime(p) n = p * q e = 0x10001 d = inverse(e, (p-1) * (q-1)) c = pow(m, e, n) print(f"c = {c}") print(f"d = {d}")
''' c = 30552929401084215063034197070424966877689134223841680278066312021587156531434892071537248907148790681466909308002649311844930826894649057192897551604881567331228562746768127186156752480882861591425570984214512121877203049350274961809052094232973854447555218322854092207716140975220436244578363062339274396240 d = 3888417341667647293339167810040888618410868462692524178646833996133379799018296328981354111017698785761492613305545720642074067943460789584401752506651064806409949068192314121154109956133705154002323898970515811126124590603285289442456305377146471883469053362010452897987327106754665010419125216504717347373 '''
from Crypto.Util.number import * from gmpy2 import isqrt
e = 0x10001 c = 30552929401084215063034197070424966877689134223841680278066312021587156531434892071537248907148790681466909308002649311844930826894649057192897551604881567331228562746768127186156752480882861591425570984214512121877203049350274961809052094232973854447555218322854092207716140975220436244578363062339274396240 d = 3888417341667647293339167810040888618410868462692524178646833996133379799018296328981354111017698785761492613305545720642074067943460789584401752506651064806409949068192314121154109956133705154002323898970515811126124590603285289442456305377146471883469053362010452897987327106754665010419125216504717347373
ED1 = e*d - 1
for k inrange(1, e): if ED1 % k != 0: continue T = ED1 // k for r inrange(1, 5000): S = r*r + 4*T s = isqrt(S) if s*s != S: continue if (s - r) % 2 != 0: continue x = (s - r)//2 if x <= 0: continue p = x + 1 q = p + r ifnot isPrime(p) ornot isPrime(q): continue n = p*q m = pow(c, d, n) print(long_to_bytes(m)) # b'flag{Y0u_kNow_h0w_7o_f4cTor1z3_phI}'
''' N = 0x662854e5ee8b1aa73eea7c897f0f1bd7cace486dea68fb4e9b1affe86ddae225221e9941b7e90b7dd87d57988fc3428f51433a5c2a6e7ef9cbe85aace0925914347ca1d403ea58e2f36435b67648f8caf0abd29c9c24d3caeadab2c41522deda75c19584ec917fa683ff16c932f334db3145a8367c3dc6bc3b918ff3f69f8bfb16c45b4caab1e8ecef24e8e923e984e921115d9fb997a638c8e25d74d592f279359e7147745a7a8443603287120d1a186f30d5a41ce26545f85844721b788564e306791ae39c3be23aeeab010e79302afab4b3e9ab18cb2769382ff8fcbc0514f51861ec6db247f0a0343b7cc6d44299878f7006c118df10de6937c11e3aed7d e = 0x58a2680eae331e41397475dd699a75f242897e4ed4048338137eb40100cc406b651c4518f4057ad8419cd6a82605113dd5801cd9f022f8bda424b02db5feb333d96636026c3ffc4cab74f7426aa14fb1139663a4f6248dd8e5c7075fcdf3e520c425697775cfb65d33ccca5ffe08d944753b1e9da2dbf96713ece5436deb6dbc843dcd5c497eda9919e055a32c76798770535c6a91ae00b971f35be1ab9e48dd4c701026e0744826001f6fb30e4f68d6e4981aa5a5bbcc995a9e46a4d9b1658348d0fb3b1314fa091251ea1b7379a854a3860fcba2ace323dca8157008d80d6035fd6c880404495f933bf4b4ae829b35823450a921f64b9cf63ae861b3fc4ef7 c = 0x47d2e297294af43a9a02d465f7f5272cab0af2445cbc6022def1098e075dcfb3a7830f09df6112a9fa55b34ed4d0baebad54ea2cbd32e4367cbe7a138409a0ef4c36d837ea7817ec3624fca3a19c1377eaf08e4a519de73cb2c5e99ec8f3998e04d4c3bc44a6f1eb389111bf7c72c68bf1dd743e656467d1ecdd314b37313963758634b83ea96724b1872367a922788f2c8a046c76ccc57e86686bedd7ac431f92b9e2f1fae79701fa0d14d2a0119860c8908336c6caec87b9733f626166373631e1e7e9ba6be92d712e84e821e0e4dc105d460c6640498aefaeb5146d0f57b8e57c3e24bc13f3e79082172c1690428eb49bc6035f1e60f6a579129a2da00c60 '''
N = int(0x662854e5ee8b1aa73eea7c897f0f1bd7cace486dea68fb4e9b1affe86ddae225221e9941b7e90b7dd87d57988fc3428f51433a5c2a6e7ef9cbe85aace0925914347ca1d403ea58e2f36435b67648f8caf0abd29c9c24d3caeadab2c41522deda75c19584ec917fa683ff16c932f334db3145a8367c3dc6bc3b918ff3f69f8bfb16c45b4caab1e8ecef24e8e923e984e921115d9fb997a638c8e25d74d592f279359e7147745a7a8443603287120d1a186f30d5a41ce26545f85844721b788564e306791ae39c3be23aeeab010e79302afab4b3e9ab18cb2769382ff8fcbc0514f51861ec6db247f0a0343b7cc6d44299878f7006c118df10de6937c11e3aed7d) e = int(0x58a2680eae331e41397475dd699a75f242897e4ed4048338137eb40100cc406b651c4518f4057ad8419cd6a82605113dd5801cd9f022f8bda424b02db5feb333d96636026c3ffc4cab74f7426aa14fb1139663a4f6248dd8e5c7075fcdf3e520c425697775cfb65d33ccca5ffe08d944753b1e9da2dbf96713ece5436deb6dbc843dcd5c497eda9919e055a32c76798770535c6a91ae00b971f35be1ab9e48dd4c701026e0744826001f6fb30e4f68d6e4981aa5a5bbcc995a9e46a4d9b1658348d0fb3b1314fa091251ea1b7379a854a3860fcba2ace323dca8157008d80d6035fd6c880404495f933bf4b4ae829b35823450a921f64b9cf63ae861b3fc4ef7) c = int(0x47d2e297294af43a9a02d465f7f5272cab0af2445cbc6022def1098e075dcfb3a7830f09df6112a9fa55b34ed4d0baebad54ea2cbd32e4367cbe7a138409a0ef4c36d837ea7817ec3624fca3a19c1377eaf08e4a519de73cb2c5e99ec8f3998e04d4c3bc44a6f1eb389111bf7c72c68bf1dd743e656467d1ecdd314b37313963758634b83ea96724b1872367a922788f2c8a046c76ccc57e86686bedd7ac431f92b9e2f1fae79701fa0d14d2a0119860c8908336c6caec87b9733f626166373631e1e7e9ba6be92d712e84e821e0e4dc105d460c6640498aefaeb5146d0f57b8e57c3e24bc13f3e79082172c1690428eb49bc6035f1e60f6a579129a2da00c60)
""" Setting debug to true will display more informations about the lattice, the bounds, the vectors... """ debug = False
""" Setting strict to true will stop the algorithm (and return (-1, -1)) if we don't have a correct upperbound on the determinant. Note that this doesn't necesseraly mean that no solutions will be found since the theoretical upperbound is usualy far away from actual results. That is why you should probably use `strict = False` """ strict = False
""" This is experimental, but has provided remarkable results so far. It tries to reduce the lattice as much as it can while keeping its efficiency. I see no reason not to use this option, but if things don't work, you should try disabling it """ helpful_only = True dimension_min = 7# stop removing if lattice reaches that dimension
# display stats on helpful vectors defhelpful_vectors(BB, modulus): nothelpful = 0 for ii inrange(BB.dimensions()[0]): if BB[ii, ii] >= modulus: nothelpful += 1
print(nothelpful, "/", BB.dimensions()[0], " vectors are not helpful")
# display matrix picture with 0 and X defmatrix_overview(BB, bound): for ii inrange(BB.dimensions()[0]): a = ('%02d ' % ii) for jj inrange(BB.dimensions()[1]): a += '0'if BB[ii, jj] == 0else'X' if BB.dimensions()[0] < 60: a += ' ' if BB[ii, ii] >= bound: a += '~' print(a)
# tries to remove unhelpful vectors # we start at current = n-1 (last vector) defremove_unhelpful(BB, monomials, bound, current): # end of our recursive function if current == -1or BB.dimensions()[0] <= dimension_min: return BB
# we start by checking from the end for ii inrange(current, -1, -1): # if it is unhelpful: if BB[ii, ii] >= bound: affected_vectors = 0 affected_vector_index = 0 # let's check if it affects other vectors for jj inrange(ii + 1, BB.dimensions()[0]): # if another vector is affected: # we increase the count if BB[jj, ii] != 0: affected_vectors += 1 affected_vector_index = jj
# level:0 # if no other vectors end up affected # we remove it if affected_vectors == 0: # print("* removing unhelpful vector", ii) BB = BB.delete_columns([ii]) BB = BB.delete_rows([ii]) monomials.pop(ii) BB = remove_unhelpful(BB, monomials, bound, ii - 1) return BB
# level:1 # if just one was affected we check # if it is affecting someone else elif affected_vectors == 1: affected_deeper = True for kk inrange(affected_vector_index + 1, BB.dimensions()[0]): # if it is affecting even one vector # we give up on this one if BB[kk, affected_vector_index] != 0: affected_deeper = False # remove both it if no other vector was affected and # this helpful vector is not helpful enough # compared to our unhelpful one if affected_deeper andabs(bound - BB[affected_vector_index, affected_vector_index]) < abs( bound - BB[ii, ii]): # print("* removing unhelpful vectors", ii, "and", affected_vector_index) BB = BB.delete_columns([affected_vector_index, ii]) BB = BB.delete_rows([affected_vector_index, ii]) monomials.pop(affected_vector_index) monomials.pop(ii) BB = remove_unhelpful(BB, monomials, bound, ii - 1) return BB # nothing happened return BB
""" Returns: * 0,0 if it fails * -1,-1 if `strict=true`, and determinant doesn't bound * x0,y0 the solutions of `pol` """
defboneh_durfee(pol, modulus, mm, tt, XX, YY): """ Boneh and Durfee revisited by Herrmann and May finds a solution if: * d < N^delta * |x| < e^delta * |y| < e^0.5 whenever delta < 1 - sqrt(2)/2 ~ 0.292 """
# substitution (Herrman and May) PR.<u,x,y> = PolynomialRing(ZZ) Q = PR.quotient(x * y + 1 - u) # u = xy + 1 polZ = Q(pol).lift()
UU = XX * YY + 1
# x-shifts gg = [] for kk inrange(mm + 1): for ii inrange(mm - kk + 1): xshift = x ^ ii * modulus ^ (mm - kk) * polZ(u, x, y) ^ kk gg.append(xshift) gg.sort()
# x-shifts list of monomials monomials = [] for polynomial in gg: for monomial in polynomial.monomials(): if monomial notin monomials: monomials.append(monomial) monomials.sort()
# y-shifts (selected by Herrman and May) for jj inrange(1, tt + 1): for kk inrange(floor(mm / tt) * jj, mm + 1): yshift = y ^ jj * polZ(u, x, y) ^ kk * modulus ^ (mm - kk) yshift = Q(yshift).lift() gg.append(yshift) # substitution
# y-shifts list of monomials for jj inrange(1, tt + 1): for kk inrange(floor(mm / tt) * jj, mm + 1): monomials.append(u ^ kk * y ^ jj)
# construct lattice B nn = len(monomials) BB = Matrix(ZZ, nn) for ii inrange(nn): BB[ii, 0] = gg[ii](0, 0, 0) for jj inrange(1, ii + 1): if monomials[jj] in gg[ii].monomials(): BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU, XX, YY)
# Prototype to reduce the lattice if helpful_only: # automatically remove BB = remove_unhelpful(BB, monomials, modulus ^ mm, nn - 1) # reset dimension nn = BB.dimensions()[0] if nn == 0: print("failure") return0, 0
# check if vectors are helpful if debug: helpful_vectors(BB, modulus ^ mm)
# check if determinant is correctly bounded det = BB.det() bound = modulus ^ (mm * nn) if det >= bound: # print("We do not have det < bound. Solutions might not be found.") # print("Try with highers m and t.") if debug: diff = (log(det) - log(bound)) / log(2) # print("size det(L) - size e^(m*n) = ", floor(diff)) if strict: return -1, -1 else: print("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)")
# display the lattice basis if debug: matrix_overview(BB, modulus ^ mm)
# LLL if debug: print("optimizing basis of the lattice via LLL, this can take a long time")
BB = BB.LLL()
if debug: print("LLL is done!")
# transform vector i & j -> polynomials 1 & 2 if debug: print("looking for independent vectors in the lattice") found_polynomials = False
for pol1_idx inrange(nn - 1): for pol2_idx inrange(pol1_idx + 1, nn): # for i and j, create the two polynomials PR.<w,z> = PolynomialRing(ZZ) pol1 = pol2 = 0 for jj inrange(nn): pol1 += monomials[jj](w * z + 1, w, z) * BB[pol1_idx, jj] / monomials[jj](UU, XX, YY) pol2 += monomials[jj](w * z + 1, w, z) * BB[pol2_idx, jj] / monomials[jj](UU, XX, YY)
# are these good polynomials? if rr.is_zero() or rr.monomials() == [1]: continue else: # print("found them, using vectors", pol1_idx, "and", pol2_idx) found_polynomials = True break if found_polynomials: break
ifnot found_polynomials: # print("no independant vectors could be found. This should very rarely happen...") return0, 0
rr = rr(q, q)
# solutions soly = rr.roots()
iflen(soly) == 0: # print("Your prediction (delta) is too small") return0, 0
soly = soly[0][0] ss = pol1(q, soly) solx = ss.roots()[0][0]
# return solx, soly
delta = .271# this means that d < N^delta m = 8# size of the lattice (bigger the better/slower) t = int((1 - 2 * delta) * m) # optimization from Herrmann and May X = 2 * floor(N ^ delta) # this _might_ be too much Y = floor(N ^ (1 / 2)) # correct if p, q are ~ same size P.<x,y> = PolynomialRing(ZZ) A = int((N + 1) / 2) pol = 1 + x * (A + y)
solx, soly = boneh_durfee(pol, e, m, t, X, Y)
d = int(pol(solx, soly) / e) print(d)
m = power_mod(c, d, N)
print(bytes.fromhex(hex(m)[2:]))
''' det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found) 76516802127572529241860569119773645337201291188788443592272413818606050201799 b'flag{Y0u_kNoW_C0n7lNu3d_Fr4c71on!}' '''
from Crypto.Util.number import * from fractions import Fraction
N = int(0x662854e5ee8b1aa73eea7c897f0f1bd7cace486dea68fb4e9b1affe86ddae225221e9941b7e90b7dd87d57988fc3428f51433a5c2a6e7ef9cbe85aace0925914347ca1d403ea58e2f36435b67648f8caf0abd29c9c24d3caeadab2c41522deda75c19584ec917fa683ff16c932f334db3145a8367c3dc6bc3b918ff3f69f8bfb16c45b4caab1e8ecef24e8e923e984e921115d9fb997a638c8e25d74d592f279359e7147745a7a8443603287120d1a186f30d5a41ce26545f85844721b788564e306791ae39c3be23aeeab010e79302afab4b3e9ab18cb2769382ff8fcbc0514f51861ec6db247f0a0343b7cc6d44299878f7006c118df10de6937c11e3aed7d) e = int(0x58a2680eae331e41397475dd699a75f242897e4ed4048338137eb40100cc406b651c4518f4057ad8419cd6a82605113dd5801cd9f022f8bda424b02db5feb333d96636026c3ffc4cab74f7426aa14fb1139663a4f6248dd8e5c7075fcdf3e520c425697775cfb65d33ccca5ffe08d944753b1e9da2dbf96713ece5436deb6dbc843dcd5c497eda9919e055a32c76798770535c6a91ae00b971f35be1ab9e48dd4c701026e0744826001f6fb30e4f68d6e4981aa5a5bbcc995a9e46a4d9b1658348d0fb3b1314fa091251ea1b7379a854a3860fcba2ace323dca8157008d80d6035fd6c880404495f933bf4b4ae829b35823450a921f64b9cf63ae861b3fc4ef7) c = int(0x47d2e297294af43a9a02d465f7f5272cab0af2445cbc6022def1098e075dcfb3a7830f09df6112a9fa55b34ed4d0baebad54ea2cbd32e4367cbe7a138409a0ef4c36d837ea7817ec3624fca3a19c1377eaf08e4a519de73cb2c5e99ec8f3998e04d4c3bc44a6f1eb389111bf7c72c68bf1dd743e656467d1ecdd314b37313963758634b83ea96724b1872367a922788f2c8a046c76ccc57e86686bedd7ac431f92b9e2f1fae79701fa0d14d2a0119860c8908336c6caec87b9733f626166373631e1e7e9ba6be92d712e84e821e0e4dc105d460c6640498aefaeb5146d0f57b8e57c3e24bc13f3e79082172c1690428eb49bc6035f1e60f6a579129a2da00c60)
defwienerAttack(N, e, c): res = [] rres = [] cf = continued_fraction(Integer(e) / Integer(N)) convers = cf.convergents() for pkds in convers: pk, pds = pkds.as_integer_ratio() if pk == 0or pds == 0or pds == 1: continue if (e * pds - 1) % pk != 0: continue print(pds) m = pow(c,pds,N) mes = long_to_bytes(m) if mes.endswith(b"}"): print(pds.bit_length()) return mes, pds, pk
res = wienerAttack(N,e,c) assert res mes,d,k = res print(mes) ''' 76516802127572529241860569119773645337201291188788443592272413818606050201799 256 b'flag{Y0u_kNoW_C0n7lNu3d_Fr4c71on!}' '''
LWECC
Easy ECC…and LWE maybe
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
from Crypto.Util.number import * from Crypto.Cipher import AES from random import choice from hashlib import md5 from secret import flag
p = 1096126227998177188652856107362412783873814431647 E = EllipticCurve(GF(p), [0, 5])
s = [E.random_element() for _ inrange(73)] e = [E.random_element() for _ in"01"] A = random_matrix(GF(p), 137, 73) b = [(sum(i*j for i,j inzip(_,s)) + choice(e)).xy() for _ in A]
deflog_candidate(P): if P == E(0): return0 x_P = ZZ(P.xy()[0]) y_P = ZZ(P.xy()[1]) try: P_lift = Eq.lift_x(x_P, all=True)[0] except Exception: returnNone if (P_lift.xy()[1] - y_P).valuation() < 1: P_lift = -P_lift R = p * P_lift if R == Eq(0): returnNone x_R, y_R = R.xy() val = -x_R / y_R v = val.valuation() if v < 1: returnNone if v >= 2: res = val / p**2 else: res = val / p return GF(p)(res.residue())
raise Exception("Could not find working Smart Log map")
_SMART_LOG = _find_smart_log_func(E)
defto_scalar(P): ifisinstance(P, (tuple, list)): P = E(P) v = _SMART_LOG(P) if v isNone: raise ValueError("Smart log failed for this point") returnint(v)
deffrom_scalar(k, G): g = to_scalar(G) if g == 0: raise ValueError("to_scalar(G)=0; choose a different generator G") return (k * inverse_mod(g, p)) * G
b = vector(GF(p), [to_scalar(P) for P in b]) A = matrix(GF(p), 137, 73, A)
P_tmp = E.random_element() while P_tmp == E(0): P_tmp = E.random_element() G = inverse_mod(to_scalar(P_tmp), p) * P_tmp
defprimal_attack2(A,b,m,n,p): L = block_matrix( [ [(matrix(Zmod(p), A).T).echelon_form().change_ring(ZZ), 0], [matrix.zero(m - n, n).augment(matrix.identity(m - n) * p), 0], [matrix(ZZ, b).stack(vector(ZZ, [1]*nums)), 1], ] ) Q = diagonal_matrix(ZZ, [p]*m + [1]*2) L = L*Q L = flatter(L) L = L/Q L = Matrix(ZZ, L) res = vector(Zmod(p), L[2]) e, k, t = res[:-2], res[-2], res[-1] return (e-vector(ZZ, [t]*nums))*inverse(k,p)
e2 = primal_attack2(A, b, nums, n, p) # print(e2) s = A[:nums].solve_right(b[:nums]-e2[:nums]) # print(s)
assert b == A*s + e2
s_points = [from_scalar(int(k), G) for k in s] key = md5(str(s_points).encode()).digest() plain = AES.new(key=key, nonce=b"LWECC", mode=AES.MODE_CTR).decrypt(enc) print(plain) # b'flag{9fa22070eb9ddfe4085c432f04019b}'