# CryptoHack Writeup ## INTRODUCTION **1. Finding Flags** Each challenge is designed to help introduce you to a new piece of cryptography. Solving a challenge will require you to find a "flag". These flags will usually be in the format crypto{y0ur_f1rst_fl4g}. The flag format helps you verify that you found the correct solution. Try submitting this into the form below to solve your first challenge. ``` Writeup: Flag: crypto{y0ur_f1rst_fl4g} ``` # ****MATHEMATICS**** ### `> Qadratic Residues` Quadratic residual An integer is called quadratic residual if.. a^2=x % p ——> (a^2 - x)=k*p ——> [1] p-integer modulo where Zp={0..p-1} and Z*p={set of integers where GCD(p, Zp)==1} Now solve Eq-1 Here, Z*p are "a" values --- ```python p=29 ints = [14, 6, 11] from Crypto.Util.number import * a_val=[i for i in range(0,p-1) if GCD(p,i)==1] for i in a_val: for j in ints: for k in range(0,100): if (pow(i,2)-j)==k*p: print(i) #We later go for 8 as it is the smallest among all. ``` ### `> Legendre Symbol` Legendre Symbol obeys these following rules, $(a / p) = 1$ if a is a quadratic residue and a ≢ 0 mod p $(a / p) = -1$ if a is a quadratic non-residue mod p $(a / p) = 0$ if a ≡ 0 mod p Where (a / p) == (a^(p-1)/2) % p, this will give you all the Legendre's Symbols The prime supplied obeys p = 3 mod 4, which allows us easily compute the square root. So from this, we can take (p+1/4). (a/p) = a**(p+1/4) mod p —→ for p = 3(mod 4) > According to Tonelli–Shanks algorithm, p = 3%4 this makes the computation much easier. Tonelli factorization doesn’t work on composite( Non-Prime ) number. > ```python p = 101524035174539890485408575671085261788758965189060164484385690801466167356667036677932998889725476582421738788500738738503134356158197247473850273565349249573867251280253564698939768700489401960767007716413932851838937641880157263936985954881657889497583485535527613578457628399173971810541670838543309159139 ints = [25081841204695904475894082974192007718642931811040324543182130088804239047149283334700530600468528298920930150221871666297194395061462592781551275161695411167049544771049769000895119729307495913024360169904315078028798025169985966732789207320203861858234048872508633514498384390497048416012928086480326832803, 45471765180330439060504647480621449634904192839383897212809808339619841633826534856109999027962620381874878086991125854247108359699799913776917227058286090426484548349388138935504299609200377899052716663351188664096302672712078508601311725863678223874157861163196340391008634419348573975841578359355931590555, 17364140182001694956465593533200623738590196990236340894554145562517924989208719245429557645254953527658049246737589538280332010533027062477684237933221198639948938784244510469138826808187365678322547992099715229218615475923754896960363138890331502811292427146595752813297603265829581292183917027983351121325, 14388109104985808487337749876058284426747816961971581447380608277949200244660381570568531129775053684256071819837294436069133592772543582735985855506250660938574234958754211349215293281645205354069970790155237033436065434572020652955666855773232074749487007626050323967496732359278657193580493324467258802863, 4379499308310772821004090447650785095356643590411706358119239166662089428685562719233435615196994728767593223519226235062647670077854687031681041462632566890129595506430188602238753450337691441293042716909901692570971955078924699306873191983953501093343423248482960643055943413031768521782634679536276233318, 85256449776780591202928235662805033201684571648990042997557084658000067050672130152734911919581661523957075992761662315262685030115255938352540032297113615687815976039390537716707854569980516690246592112936796917504034711418465442893323439490171095447109457355598873230115172636184525449905022174536414781771, 50576597458517451578431293746926099486388286246142012476814190030935689430726042810458344828563913001012415702876199708216875020997112089693759638454900092580746638631062117961876611545851157613835724635005253792316142379239047654392970415343694657580353333217547079551304961116837545648785312490665576832987, 96868738830341112368094632337476840272563704408573054404213766500407517251810212494515862176356916912627172280446141202661640191237336568731069327906100896178776245311689857997012187599140875912026589672629935267844696976980890380730867520071059572350667913710344648377601017758188404474812654737363275994871, 4881261656846638800623549662943393234361061827128610120046315649707078244180313661063004390750821317096754282796876479695558644108492317407662131441224257537276274962372021273583478509416358764706098471849536036184924640593888902859441388472856822541452041181244337124767666161645827145408781917658423571721, 18237936726367556664171427575475596460727369368246286138804284742124256700367133250078608537129877968287885457417957868580553371999414227484737603688992620953200143688061024092623556471053006464123205133894607923801371986027458274343737860395496260538663183193877539815179246700525865152165600985105257601565] from Crypto.Util.number import * for a in ints: if pow(a,(p-1)//2,p)==1: non_res=a print(non_res) #Now Finding roots of the non residuel value new_val=pow(non_res,(p+1)//4,p) print(new_val) ``` ### `> Chinese Remainder Theorem` ```python #import gmpy2 ''' Given the following set of linear congruences: x ≡ 2 mod 5 x ≡ 3 mod 11 x ≡ 5 mod 17 Find the integer a such that x ≡ a mod 935 ''' from Crypto.Util.number import * def Chinese(n, N, a): result = 0 for i in range(len(n)): ai = a[i] ni = n[i] bi = N // ni result += ai * bi * inverse(bi, ni) return result % N N=935 n=[5,11,17] a=[2,3,5] result=Chinese(n,N,a) print(result) ``` ## Brainteaser Part 1 ### `> Successive Powers` ```python from Crypto.Util.number import * import gmpy2 t=[588, 665, 216, 113, 642, 4, 836, 114, 851, 492, 819, 237] pval=max(t)+1 for p in range(pval,1000000): try: x=[ (t[i] * inverse(t[i-1]) ,p)%p for i in range(1000) ] if len(set(x))==1: print(p,x) except: pass ``` ### `> Broken RSA` ```python import Crypto.Util.number as cun from pprint import pprint def roots_of_unity(e, phi, n, rounds=500): # Divide common factors of `phi` and `e` until they're coprime. phi_coprime = phi while cun.GCD(phi_coprime, e) != 1: phi_coprime //= cun.GCD(phi_coprime, e) # Don't know how many roots of unity there are, so just try and collect a bunch roots = set(pow(i, phi_coprime, n) for i in range(1, rounds)) assert all(pow(root, e, n) == 1 for root in roots) return roots, phi_coprime e = 16 n=27772857409875257529415990911214211975844307184430241451899407838750503024323367895540981606586709985980003435082116995888017731426634845808624796292507989171497629109450825818587383112280639037484593490692935998202437639626747133650990603333094513531505209954273004473567193235535061942991750932725808679249964667090723480397916715320876867803719301313440005075056481203859010490836599717523664197112053206745235908610484907715210436413015546671034478367679465233737115549451849810421017181842615880836253875862101545582922437858358265964489786463923280312860843031914516061327752183283528015684588796400861331354873 c = 11303174761894431146735697569489134747234975144162172162401674567273034831391936916397234068346115459134602443963604063679379285919302225719050193590179240191429612072131629779948379821039610415099784351073443218911356328815458050694493726951231241096695626477586428880220528001269746547018741237131741255022371957489462380305100634600499204435763201371188769446054925748151987175656677342779043435047048130599123081581036362712208692748034620245590448762406543804069935873123161582756799517226666835316588896306926659321054276507714414876684738121421124177324568084533020088172040422767194971217814466953837590498718 # Problem: e and phi are not coprime - d does not exist phi = n-1 # there were no prime factors of n # Find e'th roots of unity modulo n roots, phi_coprime = roots_of_unity(e, phi, n) # Use our `phi_coprime` to get one possible plaintext d = pow(e, -1, phi_coprime) m = pow(c, d, n) assert pow(m, e, n) == c # Use the roots of unity to get all other possible plaintexts ms = [(m * root) % n for root in roots] ms = [cun.long_to_bytes(m) for m in ms] pprint(ms) ``` # ****SYMMETRIC CIPHERS**** ### `> ECB CBC WTF` In the given challenge the given mode encryption was in CBC but decryption with ECB. ![Untitled](Cryptohack%20Writeup%200dd7fcfd99fc48ebaa3daccef67c13d5/Untitled.png) `ciphertext = iv.hex() + encrypted.hex()` The generated ciphertext in my case was `"cb30b6a41ea0302796321785baa60db2af7989205ddcbac9a207e202970885d8a275d6e53e3effcf527bf9bbf1cf87a1"` We see the length of the ciphertext to be 96 in hexadecimal, 48 in bytes where Initialization vector (iv) length is of 16 and the remaining is the actual ciphertext. `iv=ciphertext[:16]` `ct=ciphetext[16:]` Now decrypt the first 16 bytes ct block with ECB mode and then xor with iv, this is the first part of the plaintext and the second 16 bytes block will be decrypted with ECB mode and that text is now xored with the first ct block resulting in the 2nd plaintext. `plaintext=p1+p2` --- ### `> Flipping Cookie` ![Untitled](Cryptohack%20Writeup%200dd7fcfd99fc48ebaa3daccef67c13d5/Untitled.png) ### Ciphertext {"cookie":"29ba24b9e53b8f4852ce8daa7fbad6de8cf8813d64f394306815baa49b7c3fc323c646c6455fbf3b920455962deb81d9"} ### Source Code ```python from Crypto.Cipher import AES import os from Crypto.Util.Padding import pad, unpad from datetime import datetime, timedelta KEY = ? FLAG = ? @chal.route('/flipping_cookie/check_admin/<cookie>/<iv>/') def check_admin(cookie, iv): cookie = bytes.fromhex(cookie) iv = bytes.fromhex(iv) try: cipher = AES.new(KEY, AES.MODE_CBC, iv) decrypted = cipher.decrypt(cookie) unpadded = unpad(decrypted, 16) except ValueError as e: return {"error": str(e)} if b"admin=True" in unpadded.split(b";"): return {"flag": FLAG} else: return {"error": "Only admin can read the flag"} @chal.route('/flipping_cookie/get_cookie/') def get_cookie(): expires_at = (datetime.today() + timedelta(days=1)).strftime("%s") cookie = f"admin=False;expiry={expires_at}".encode() iv = os.urandom(16) padded = pad(cookie, 16) cipher = AES.new(KEY, AES.MODE_CBC, iv) encrypted = cipher.encrypt(padded) ciphertext = iv.hex() + encrypted.hex() return {"cookie": ciphertext} ``` ### `> Lazy CBC` ![Untitled](Cryptohack%20Writeup%200dd7fcfd99fc48ebaa3daccef67c13d5/Untitled.png) ### Ciphertext ct="339451173da40bb3f2599e0adcb474759c89a29d7eb5ece460037fd4158c3cd8af8f76d0164ebe3b676b28e1eff93508” ### Source Code ```python from Crypto.Cipher import AES KEY = ? FLAG = ? @chal.route('/lazy_cbc/encrypt/<plaintext>/') def encrypt(plaintext): plaintext = bytes.fromhex(plaintext) if len(plaintext) % 16 != 0: return {"error": "Data length must be multiple of 16"} cipher = AES.new(KEY, AES.MODE_CBC, KEY) encrypted = cipher.encrypt(plaintext) return {"ciphertext": encrypted.hex()} @chal.route('/lazy_cbc/get_flag/<key>/') def get_flag(key): key = bytes.fromhex(key) if key == KEY: return {"plaintext": FLAG.encode().hex()} else: return {"error": "invalid key"} @chal.route('/lazy_cbc/receive/<ciphertext>/') def receive(ciphertext): ciphertext = bytes.fromhex(ciphertext) if len(ciphertext) % 16 != 0: return {"error": "Data length must be multiple of 16"} cipher = AES.new(KEY, AES.MODE_CBC, KEY) decrypted = cipher.decrypt(ciphertext) try: decrypted.decode() # ensure plaintext is valid ascii except UnicodeDecodeError: return {"error": "Invalid plaintext: " + decrypted.hex()} return {"success": "Your message has been received"} ``` ### Attack `Given the conditions above we can exploit this in the following way:` 1. `Make a plaintext with a length of *at least* 3 blocks` 2. `Encrypt the plaintext and get the resulting ciphertext` 3. `Modify the second block of the ciphertext to *contain only zeros*` 4. `Modify the third block of the ciphertext to be *the same as the first block*` 5. `Decrypt the ciphertext and get the *invalid plaintext* result` 6. `XOR the first and third blocks of the *invalid plaintext*` 7. `That’s our key!` As IV = Key let’s say, `Pt1 = Key ^ Decrypt(Ct1)` `Pt2 = Ct1 ^ Decrypt(Ct2)` `Pt3 = Ct2 ^ Decrypt(Ct3)` If we make Ct2 = 0 [Block] and C3 = C1 then, `Pt1 = Key ^ Decrypt(Ct1)` `Pt2 = Ct1 ^ Decrypt(0)` `Pt3 = 0 ^ Decrypt(Ct1)` Therefore, `Key = XOR(Pt1,Pt3)` ```python ct="339451173da40bb3f2599e0adcb474759c89a29d7eb5ece460037fd4158c3cd8af8f76d0164ebe3b676b28e1eff93508" ct=ct.encode() b1=ct[:32] b2='0'*32.encode() new_ct=b1+b2+b1 print("New fake Cipher: ", new_ct) #after decryption of new_ct we get a plaintext pt="61616161616161616161616161616161ff2f3668967735aa1d493ff1f1c4ff39c5153f87bbf70ac3705e5c4ffd5e7a14" pt1=pt[:32] pt3=pt[64:] key=xor(bytes.fromhex(pt1),bytes.fromhex(p3)).hex() print("Key:", key) #use this key to get the FLAG ``` ### `> Triple DES` ### Source Code ```python from Crypto.Cipher import DES3 from Crypto.Util.Padding import pad IV = os.urandom(8) FLAG = ? def xor(a, b): # xor 2 bytestrings, repeating the 2nd one if necessary return bytes(x ^ y for x,y in zip(a, b * (1 + len(a) // len(b)))) @chal.route('/triple_des/encrypt/<key>/<plaintext>/') def encrypt(key, plaintext): try: key = bytes.fromhex(key) plaintext = bytes.fromhex(plaintext) plaintext = xor(plaintext, IV) cipher = DES3.new(key, DES3.MODE_ECB) ciphertext = cipher.encrypt(plaintext) ciphertext = xor(ciphertext, IV) return {"ciphertext": ciphertext.hex()} except ValueError as e: return {"error": str(e)} @chal.route('/triple_des/encrypt_flag/<key>/') def encrypt_flag(key): return encrypt(key, pad(FLAG.encode(), 8).hex()) ``` There is a serious Vulnerability with DES, it is vulnerable to [Weak Keys](https://en.wikipedia.org/wiki/Weak_key) . This vulnerability was discovered when the key was brute forced within a day or two in 1990s. Let me demonstrate by solving this challenge. There are various weak keys that can be used for attack, not necessary to use my key alone. ### Attack The key that I used to encrypt was “`0101010101010101FEFEFEFEFEFEFEFE`". Now we obtained the cipher text {"ciphertext":"63727970746f7b6e30745f346c6c5f6b3379735f3472335f673030645f6b3379737d060606060606"} Use the same ciphertext to and key to encrypt again, this gives us the **FLAG**. ### `> Symmetry` ### Source Code ```python from Crypto.Cipher import AES KEY = ? FLAG = ? @chal.route('/symmetry/encrypt/<plaintext>/<iv>/') def encrypt(plaintext, iv): plaintext = bytes.fromhex(plaintext) iv = bytes.fromhex(iv) if len(iv) != 16: return {"error": "IV length must be 16"} cipher = AES.new(KEY, AES.MODE_OFB, iv) encrypted = cipher.encrypt(plaintext) ciphertext = encrypted.hex() return {"ciphertext": ciphertext} @chal.route('/symmetry/encrypt_flag/') def encrypt_flag(): iv = os.urandom(16) cipher = AES.new(KEY, AES.MODE_OFB, iv) encrypted = cipher.encrypt(FLAG.encode()) ciphertext = iv.hex() + encrypted.hex() return {"ciphertext": ciphertext} ``` AES OFB mode is symmetrical cipher, so just by using the same IV and Ciphertext we can get the Flag. ### `> Bean Counter` ### Source Code ```python from Crypto.Cipher import AES KEY = ? class StepUpCounter(object): def __init__(self, value=os.urandom(16), step_up=False): self.value = value.hex() self.step = 1 self.stup = step_up def increment(self): if self.stup: self.newIV = hex(int(self.value, 16) + self.step) else: self.newIV = hex(int(self.value, 16) - self.stup) self.value = self.newIV[2:len(self.newIV)] return bytes.fromhex(self.value.zfill(32)) def __repr__(self): self.increment() return self.value @chal.route('/bean_counter/encrypt/') def encrypt(): cipher = AES.new(KEY, AES.MODE_ECB) ctr = StepUpCounter() out = [] with open("challenge_files/bean_flag.png", 'rb') as f: block = f.read(16) while block: keystream = cipher.encrypt(ctr.increment()) xored = [a^b for a, b in zip(block, keystream)] out.append(bytes(xored).hex()) block = f.read(16) return {"encrypted": ''.join(out)} ``` `def __init__(self, value=os.urandom(16), step_up=False):` . Here steup_up = False means the value is not getting updated. Let’s understand the working of the code, the first block header of the png file is XORed with the key and so on. ```python ct='649b00e86c44d40680028e6a09bc6d9cedcb4f83614ece7b88048e67404d6ab318cb4ebe02349a54f450ef1060845ba18ba222ca413ab77ce522eb1f299229ce95118b360adcea3b0e8671a0690221b26cebc1ec73ee17d0e08d3178e24673a4a4e89de702f853910f880483438491f5b544b140dea03d337c693bc2e77da7f09684b2c4d2837cf4b0d059d8b54acf1a331cb9f058d181f567e9018da7bfac85783cb45a82a651e74ef74c9aa587d4b2502c8bda989b8edfbfbd5658bf9654c8c7287d6e86b43948b57dc3f77ef71625de9b6330f89519c37b69d5ce8f5596102f3292d05ef1b1cf40f107f016415d27b4d0e102459bf0f5bc48e42d9cd83fb4a252bf92b8d14f7273f17d1a2b95640b8665c778706562534fc73dd9efb8e821ea5405c2dda834d5b2dba428622c50b02a3e81509c9704936fa7f135bea763b191310305dab2f0df4ff905881b1e46fc39b4f25080bbe2b3dc7df0fa92c3467173723191f4d1618311ecc11cbe3a73f2027e3f001ea3a0b94e947500132b72b2d2ba912109f311e6bc9629c35135a70909fa662662c0c29ced1ca9a81cbcca03c090105607311143be2e3f726f3e52e74a1f32b14f405c3d4bab1fbb567b55453b92c3fbf6344e4f656877d6d60867f5322200b12d22b7302025bd3fa6dfd6261c37db348fc34cc6b9605782cde1c2e4bca2e28d53d93d9fb2b51ff63ccb685bda0b9120beb045b0d630a608d8f33292840c460b1e053009dcf7c313a5b2c833464bfd75064e8cc88a864b830af27b01b8b981e62a37fa6b823bc6b7221d3d9f346f6b24752d5fa955d8c7691534f5b2aa3835c4f0bcbb15a2269f35bba7bc12217106f90a4cb0b88d323ac30ae7b8e0c788e8033f359a546fd80d36d7a244e4379e3b47b5bb42365a925fe0cec5cf965e07444cabead0eb187120a2e47fb8d93295921fb76075e2366779ca3b189019c404e7087c7733e499651002855e78093b862377d5ab178d12359988eeca5534578ba379bae01272758cb5f8fb8a4d237e1d91929fcd62e9cfd444fce94c78b18946e50021f28de8201107fbd145fbfce41c86efcf01e79d3336fc0c65c63ef7a6781eba33166a48f22ec9a18894da92092d51169a6bbdfe54fc51ae9bb70b0492e83de0c5a0e1686ef5f79ce358f0b46865f0b6eb7c42ac26487b1693f6c3b477b267ccfef4f5683138cfd3339a688f21589a0ff074b37cf3f586089e89f770814638d43c646909df57d56049cfe578d06a80bc3c5532dc58f6ace1b7dde7200843dc011fbd56b19abbe7152a6d9e5b5f0c973f85a7a523ef66e31252520e5953ad20f97b879c297d67decf908878a051c0e7fed468ea658c341a0ddfa1467988b8a23e956cab1a111cc992185fb7972c48d9f60f15c6bcb1629cd095e8f378b12d578ebbbb40ed1d8f9e80a58e6c83502434dc13847bd6c4bc1115ee82b0e0087fa52109d522573dae6f9c3ab9520ecaa7339ead80fcb550b58335197772ce7c44a45c90a5185eb697c23facc6d2f080b1707cac33050ea97b5811a6e63008e30c00d2552c84fd80915090128fa948d81f2dec4b436229f658bf64fba2f143c5b36dc503edb5c5fbc2f98eccb949b5ec6a5d3852d202b98766975225a3eee83b023934bc74b765445897c9d0ecdb204738ce236ad2171bc59b47084f34bfa4ab31630b4a67489e5fb2b151a06f5d9f7063eeaa4501e88b172a0fad8cbae96c385333b3b303ebd0741e2bffee38c0b95a70534b13a97aa43f4b7cf225939fb465c81c3755acaed576a3c87decf3e4e95335c27433973d0a6894dd830b484071d0c1f823e8f912a82ec1b794eaed42e6ee490aeab1c8702d259161c9010a9b8c394c5b05aaedeecb420382ab19561db6842be93f66d227b3fd7d709346fe35f609f24e3d19fc8736a8e7ff3280f8a01d78416a801a710eaf0f478b4c5fa0f35644f0a2ba5f7c0aa15fd4f842a4f91959e0316b53a26ca8ec60565af772a9915fa4361ff36b06a374703af731928b5849e791388aeff34ddc9ccae3257dfd407bb827468c832a5c4fa34292f87374213028b86a4671a32b6b2d97a42c1e39df2eefa55bc5fc53642dd130019751bf1ad2b6f82f253306939ece37c71f6d6abc5c6ce28083b598489fed29385e6acf18aba71d0d5f0fc456ed34f6cf74f803f5730c7a5df4f91ed4cc502bdf91a74e2195cd364e1b25f26c15e2e3031a80c3f4997a7dd187a5a326cd39e43978b87785933d5367aadf050776894557e4d808e5601e15a7d23b2d71b777b70e566bde53f4d7203901a77e2510ac3f2d211205a514c53c483831474906c16ce66fb6e16115b56cd9c1d180ad61738f90fa8be69b1b44180cfd2ebac158929a07eff4cf4bfdddfe421b01a220fccece09612545d306ab69527ff6831b9c91776c22911c6453d4b2baf45dfe8ca4b0a68c7966a8a780774bd2ebeb38b0549db21675c9d1049fa462cc08b00ffbb8c358583a33b35fbeb047ac6a674c8ed966b78d4fb32290d025ed1fad03c566229c3f5285539ed884c1b8fc37646edbbe8998d6a3014fb9b6954054e8266fe95a790ad427f46e76e38aa6e61a2c56dc1a725ad5bddedb302ab6429f7d4087e5dc04a96792bdb5d064ba57b1b73c0ed2d94fb60361802de1621a8bbbc3e014cb9e9896a0c31082d5ceddaf7e46da0cdd6a59cd08d88be22c7e64398ff741ed5d2ac301be114fd629953bcb668a7a9a982edee2363f38dc13c7a56371eeb2f6dbb89d47482b6c954d30b6432abf7d2241f6c8ce11d736182ec3daff4a4415495d24f14d780706ae2e8f0d709ca1e0b9bb445940d4ae62f69cbeb80fe585630ef19807849664cc280df4843234d0df7132a1f7a25d129dfebb515cabeeb63b5ba0f5ace8f725bf35a71f15ab41af88ea45db666d90aeabcd7290bb65422cc28c74e82785febee1bce89673d3673bef9b2d1c116e96864264804c30a07efa7553ec4e8aa018323cd9155d3389c2cfa44f1816cb9af5d19d54faa80c36c0f20920c03a221bdfb2e8a566af919354eddaa6742bae11e1930c1622cf14c1fcea5c483082e9c0a769c547d87a37fd20fe9648d841c1f6aa8847db2cd9bb6b515c9add7f15783a866bdb2e8facffa75509348c7ed149c705184d088a759dfda47d78f7ad83e06b7e616de595294e919e3732425fd592b813e640ee1d2e898c6d06c0f2d6317a5925575bdeb721318539f8a32f29ab684c809e2852df9868f57cc1a6106b93a18d8f16d21d3b3dd780054e7b21361e881f5f240254d5e902416328aabf1017f9c5e2abce170bd21d86140b57f0b5f0e0491f722f889e3e8fbbd8f2ed5b527e6b7b64624b3e59884bdce8a6a12192ae8e84692e16ee842f6946654204edee253eb82c8f7ab690f2119c6c52714c7b0f8374f5de4a951ff2fe755cd566f3db72f70245a5c89526fe2e9f68e1b8dd37569c91b59b81c9efbffa4e93ab8299ee73eec266c400a4b46f1b63ae0942cfbb80926156b7c16889d7a1f81220b064349c4f1c6ab76f90a7997200fe432b927fc83b6772367f25c20cbad04d1b3acf15e1247273405f822a37a2c8094f60f073142a3a00e7640c7804c7de4a8122a44e31f65163c7a8eacebed52d66f3e8cdfa5ddca5661f18fbfc8fa0297ebc84441b97b304c0012c5621bb1fcab9939069a328cc339ea314ed506626a2ef2d1ea06dd64ff3fe01f5147bc533ea828f449c177918262a760b4397e5ffbd6274ecf846ae2c7abbdab4c6d00f0a4e0530e5e380a6c3c3c8ef2240700f3da3e34c471ef8bd392834e6356eb403d6dde16978c39ecc7ebee011f113b78fdf2beb91cefb1deb44119c317a6ba33eeea7f269c1bf5263433e6b92bd808dc8f1e671ff2bd796bd8e307e755844f9e4076dbba67c1d28bc6b1b627b72329d7e03d809001796fe7ac8eac5ebc835b984ba66fe9c4bf46dd6a9fcdd5d8e7cbe4ee4bec0ba225aa77d517e670675c49a7e2e0b092b9276d51fe1765bfa28b3169ece0232982212793dafc69e683a25b7e7f08a480fe268dded0dd2de3108e4a9820c8db027b8fbc6714d5abb04489ac7c3378b09a5d114968a8389ed9ee86d30288526b84b11b4facd489b9cf7f27cd80f41ce991671524431161490df2f646051db8950b91ff0f634aaaf03bddfbd4223ccaa6ab26d058903a905927af2b3b8a6e02f9e6c83b1bcecfb60b49756cc22fcd799085f08345aae2d3191c3897724a7d5db05d520d4a60a0a3fa69a60cf12e2e9b60bfe83d242c64654198f587483eed1c4944d0119758a0a8e3e8656ca251dbb48b71d4514daf9e8873e39b8d846c6688e3573905da633b5eb70476e896c5536aa5343e842328b201b1d5a0a4ea787b8d38d7f260ec839c08df984dcbc316c392af2a92eac16a45218097518be33e13bdc70bb228022ec0abaa4b743fc99b4480894c9d4d1e5788e08550b851649adf3f1b7fc6fd857a29fe109fff211bdae36ba61bbed655584838e0d4721bf936f85d8794294ec9974f543d4225fce5a6d6ae2d9397401c6cda12b5ffca3f338b0401b42fa57412cd504cc18e1b928fdd23ab64d8659f88b937a3a980168b95a65f552c500e4e5a988bcc5df6f7937fab81dd2b8a242459284e179d5f0150e640491bf5de07742f9ea8bd12737977595b5f1e3d3a491dfb15a095348487676f10ce3992eedfa9b1f3bbc1e6ce62493a3f8e321259b31bab89f2ac6315c542c3b70fc05ec12c30b135e092cca03d5b5f00c601fac9a5c0ff89906e43f260464c374a423968f6c940c63e0926e1eba6db2a82abcb9f944153602c3ba58bac2e1ccd89d532e0959e26d5d84e6ff47e764526db1cf015b7f108b6010655242ce72711f935aae4d79cf66162599c5c843f56efdcf863d6b968755054d2ec3693cb64307ccc9dff250912a921172fb43306370ca51e91b4b7f7f0ea5c2fc623629ec33301b6957afa96484774e1b3faf1c3c57b42c43896c5dfeadea73128e0bfd483fa32788a5af54affb78e152365414493af9b01ec96e85e6fe90800d7d5a18216b6a5c8b4e250388ab6cc6ea5a79fb1b7647a5befa0c4892124dc3d210bd825e08a061c5fec639238cf2f4380ccd2013ca9ab710eff51c00940418f90ea178ae901a9e446c94052649cae1f2170025b1b1918065ce39540a592ca7701e5da54ccfcd52d20ca08aadc522f6cac9daefb8a953d0784aa820d8ab0ac9fad53e7841090da398a3ded9cf7fafc442ff04414c72f2b31f2931cf7a40fc22856856add4a346da7137cd1b9a2a7cebb6f5ef9269c12357c46b2303892f19b69be8eca66407d80ea26f9d0533d36682f645c1e1f9cec62e5d04320c9abe507ab2bffea35d81d6bbd631af080a4d4edb458d638099ee0ffee0b8bdcf73002b15cc12cde58c80be9b867e9ea93f3eb41f137c64a6823508a8b21c737946fc174729a46f397b3acdcf4b78214d3fba80852026548795029748edbba685a86ecad3241f6cd4b0e84a7c8d03f9b99c4424c31e89c836f65dade5c90d1996c6b8360ed1828cf71751cdf9692cdb180e54d2a301338f038e51ddec377a2879392507be3cf5daa7d661f37847bd6cb46b36c448d225a991ff4f86f5c44392017e6d735af6bde208db085bdad6b8ba0630dbc7cfa88745b5e4ee2a13a7e4808da544cf816ad9ae170c6e9c6ebf5ffafdddde04ea59e0ccd8e143a3febf5628cf54c1dc619db3f9300779bb0cc03dc2a2eb1f83d1578be8ccb7f6bfdd3a94e4675bb7ddfa5c83b09bee77ee21c5d5b16e73e5eef97307cf7aa292bb2771aed05cb0f24d484880bccb6d896d06dce45ab726c1bd977e9bbe92e4c47df2a3269aeebd1e0f32f1800acca6f4ad887359f4b921d29a2e6ae141f5fe73e7a3d67be1a88db518413122e0ae838ae75d1de22abeafd852397c5bea3656eb302ec36eb089a4962f1e3fbf68e5f6549277f3e476866daf37c3b3ce4dddd2d7d43286a34d756ea01688dae3dae774a0bec3297d04f0762e5902279e3db6b29995d4d020281abf0e6dd4073613e3a322f1dee53ce7bc9d51cd1273ca28e545cca60198b226ea4cfc85a39fa93e4434cb6afc8cc38c920eb1f87d731630c43aaa1065bd6f12c65e566ce1488e77fedcf3ef73aab451389131fb8e129aa1c002182959d0ba2861c408377e8ceee3b3cde42762c875736a23aa38ca6e234f38e0930a320a3ffafa4c8ac9c8149724bff852c7f77b11f775c207cc30019e01c9923a0a2ad43a0a5337d22821baca74ef0863f45bbaf2e8e5dd2001cb23a57df0eaeeb99fa36f5f63a989c374f551f83fcee931268b3400aca9310caba15df8c83cd7d6edcb4ea90305894880028e6740f4d08d56b44eaf6147be44d9718e676ed729cec3e84fd7c471b80c8002891309b96cc909c84abd7943a4c70d4b8e6740ed5d8bb5bf0dc00c23ab62f402cd1525955dab89eb39c61526ee4bc94fde30c1fa3eceedeb4ee6250f9a745aef131e10a07e341a74f37e55f37ebb32c824af92461c8c39436c9ee78aea1427d6077407933b836152c2062ddcfcc7b8ab3f41d330b3aa27016a2147dd967d2cba056de8d481765cc24fca660f186a69a415093acdc6d11e20b52d156d85bfe8cc29cdeb60523533b8d011863501754e7110a46270399ec5df84ec61d5548cc2a3dea6e2a401dac7935aa5e444d889c2a3c214f8202d5a5cab7c36017ad47d7f9e83ec85f6ae0dec500370e45ae454146bae33fd4994a687a10ccc026d9a26958ec84433a801daafe5d96cac83c320968994eb02f1c29f843333d21c7ed481900a8a87b18cfa70214f5edb153a2eee458946af88b450b7d4df44aa6d48cd7f4ec00b6c718fdfa25572f77e7ed78c096b532fc0e7f330d7f49b66bb19bc072b61e56950ad7378a458d1f716d8b62897ef52df36c170adcad136ba7c113cbc6e4a2c1f2f60e6a026e287ef2d94e6c75ade5c90734af13fe3b73f1e5b69d55597119f13eac2762b6a4b6d2c6ce743c6028e8433da7dc7d5ad5c93cd512eef9ecdfa0608e47dc914a683a548389a3e3208c6a19f4a6dd981644749c5246a50b5e0b18609e003d83f9d9c6902b5c2544d1cc284f20bcd5152824a1e0d1949d50f6c00402f2707b3ee6a78baf29945ca6ebb3715d33a1d64636b75fa2da99932f019abc87a8d216d65e8053e345b97b86cbb3715b4fa4ed2bac7b1e5595997b8de874572fe174063e2b30d082511f787aa0e1bcf88e8879755d1ab685fb5ec4884a992180361065738a2dbd01281766c5f044ecc40eb236e39cc6e0a8e971a77d202f83e50ef5b3be9bc5d93625f1972be341c430c0570ec69c68c6543af3be3d831bd3fdecc9524c23b8d7897df113e4eaf2321927b5cf2740dbf489944947ed48168e965a4fd8fdebe02157317d8f78fab53a321c88f5626a5d9be7d362d9ea4b9d0abda37eb21c12102cc4e5f1972f634b842a27103d439c2b605af650e7c576cb01aae734a954ea9aa0ec49ddc4a65b33f1d43aa8ddc02e5aeb237960b24a4dc52d08cfa0ef3a08aefe46180654bcb2dac2e8214a638a87d5e38fe2220e398ef66cf150f2a9eab5576d0aadd5812925020d167371bafa23211b7da39f7ec70b29e281b6d36825fbe9cba8f2d64453ee033cd4ca28571387947616473db3bc5763d1c8f66417ca3446f8b6eb7308d25e468c2037c7724fc1be88c09e8a589093f53f2a8f44979a443250481282fe98d6ce022ea5559ce12f57c54d120c3ecec58d5572fccebff2b4fedb530d1f187aa840d8f0bd8ffcbd1b2530eb5c5d414da1a96a028c146ae84d894b23707cee14948a243c821463ee3046bc34e24a4cc71b3bfa02cc5a014c9fe8156faf54c54d416d050dce5b2065b1a381eef0cc24d4a1c38b834a587332ebd0b070feb254acc0e82229a6d87d1119a41e39de5d1fa9421fefc7700dcce5cc4b492174789a64eab2725d23b1c9446a16275f09185a828ee448b9d6f4bffea5da2f5c788ff36ccd25edcd822cbe4ab6447e1a275d445451baf8401e84f706befa78005de91ae8f68e6642cc0e7f355f0f2a778b4e55e7ed5ecfa769f4c6e13f9f2703e4e630051320db7e5b0822ff2d5d3f570a00d0688af91d75bb2984346d16740c51e0c95f83076daede48a2cfc1b6834b058de82bf4bc74512583feac02704abfd7671c50b1c0bc7e60a2d31a4d5dabbb84338e296bd826958cb018c517628db4b123fc17cbeb27e7782d71cc225aeac29889bb4345cdc5e02b196a2a2ac262cbd1e0485d8880785813c67417343f6fc204fa5c7ddb4f4eb9c67803be6c1bf625aafc11c679c199b0704877a08f0d3d565f8cf8a0c898be3668f0871ae3c1dc0d86e010eab69a57c7686476813bbe7ea6f8e39fba419a220ac87ae1a27d062ccfbf5ecf4743664e46885ac2d9aa685a3cee1c68dc5233c82975426ac45c691f551418fa0de85c46ae20b04bf62c5d5d740a17e7042ff67cdcd41d3b50f5dace1db2daea90d64b44029b48131ecab7dc3fe760d36b476a228a46d57db770a182099fe3c134395b3654ca98e9af1c486ff543bf6d96db4381acf26017ca146f52976999ef56daf8337bb52a08411e6c3e588dcae7349fc7072b65c7b3741fa0f259314a98707cd1121c917a82408f6d5f38e62ac4310d25e4164a8a58638d2f14799f11a43a4b99a55203b81650a89fddd5829f5dada74fa78d48b2139b7f396135682cc18458e020062bcf7fb10f7d48306e721048406e430973b1da9e8ac5c1b9b7ae4d1b1f2996be70e7889d3faf9c14bd75d50d0737cf39f920addbda7c693cabf495ac468e466ae4a6df9a84f6c13f6de4258e0508865e980018abf5e7ddb4aaa64cf0ded60c20f64788edde66f49cc1f0587dc87c321b8fc4d934ea9215777bc8ca323a42aa8cc2205f14713574efbc4b66ce72589bc30d9507d6d2e9f72c260a52afc01991007a222b06c8bccef6aab98b737b4f9f1a8b40873da3f597cf94c878355e46defcb1334c02bcdeecc47adea8bac32bd3fb3af8e3a6f99badc18023bcbcb079642a27173c9954e6dcbb6559b8caa3ee1ed50da883ee30c1132bdb6a05346b13d3f6c85a2e434d36025a0c189bbb490bc7b9c6641f56187a48a06e7e90bd19a5955e6ca7eb96483a580056ce445d91c9b19056a5bc209efccaadea71a36b66c56aed7173387aa884e8a00e16f32b27060ed61e8e0540974614dbc7fa1f81ab72aaa26a7b907ce6f3e22fed3983ce2543002e0092e145b778d789763266ede2cb83a96acd6d9ea6ca8406ebde81e95578bb7d40ab992d6f971b93adb252dad2f4e9cb3ec8c3865eacbed2c5a8b9b5ec5e472f91027d6963791f5caed408aacfd1063a8e083b5698eed29ac4d017c575cd0520ee1c6f2b182cb60db7a8abb66a42842cf26418855b2fdde5baa1639b987bd197d97115579eecfe94cddd85240820ee2fe5f204571ae5e12fe1cd2cd51939f94a03aca24f99eabb735d41a631851a012bea4090725557056534c71f93a1d8806d8e02dceb4c088ae2b1713abc4f4fc3c101fa2fded082b896c3ee183a83f42c9e8d461e6a0def4d2c7caaad88f209621a1484f11cc2b3d223a8c1f16822a9331df26e1b8220f0720a5ec2807c7625fbc0387a0946d8ba80a9776b8a48dc667f19f7255711e1095c8a1119a605820d9603b20809698eefdc36ebf699e17d509de523bf84c91ae1439ab5f90bfd7f972160723c20dda29cef376b7042bab24ab290d21e3455370bf620ad11f70d4179b139079ce04d9be46853c1ca9d653e6566e4e84b114ce296a7bfe9f495ac4d855d72fdcfb8feb8ddcc36fffca9be623af45f71e79b66b8cdda49093ca30e71a0b070723c204d9be694fca0fc3eccc199517668aade978972080c7d51a350fedf044ba4c0c5565668dda0c7fb29d4db63a38a6734a8c50b72c0085c4b9d7f11a0b07f9103730ee09cee560a112a802bdbaa1eb6961449a7deeea4a814f60b6f071c59f7750c48fcea7f07594654bcb2dbe3c90caed5901e6c58b35d4da8bc864b418df381f7bcb2c8f3bb7d3a58909cf4bc9e5bfa1647042d1f77203f7c1036a43d35f765eea377e5e780df43a55b476fa84638e99be9902525e1c06e5e161e330e7ba686f93e556ec8401e009fb9458d755d58b4dae9359bf620ae22c43f437bbe3648d4ab36e89d75feae31c4185a5d4a037ded9e50b5fa53f8ea8b272223c8f41c8156e7fec4c725451ed220d09e078673f3f30875a8d81a762d31c3357bc81bfd058a4a89a7d103da3746941c173e0aa0e3ef028f413cc5a06b149d200c24e72f811c6b6887ed7eff89adcf37364ec6eeafbbdd6b8a6b530fdf74d464b4e38b935a48135283b183e9ec75518b554667a39a98eba757397de17f638de84a9088905e4c773ee560f72663b907e2a792cd7e4b695212cd062cc09e826d5e111b135a651db79e90e2deac053b06cde04901894ed883c6152d0b058ea240b563bef1cbd24269a51c468c90bea24a627e5c17ef25b3c8954d1352b76b36f0aecdd83d548febda72b4fed81ebbd3be3b85e0686d4ca56d904e39dbb3e2f256cd7dd1098498471ca1d5ba7aeff03e55ea6389ab63bd2e15984c3280e8be72a058d6be725d01fa1f10de9caf87fde75845a05163b94bc5c8114feff16981f3962f637bb597b10cd4ca2068b0885646a7fff3d48cf43a2b51ffe5f795f7a87e91b61db23a428618531ecc6e5c30ecb056a52901c445a7ca48690c1f97dcf4808fb144d79a3a835f0786fed599163173da0462f4fcf314cc737985469445e2dfbd4d4707fff79df331327e4c7e15eb37dc2e35736625fc5056c1ca53b88fda9f3bcc60389cf4d524e73eae22853e63163c50b0e1fcd39aef0b60ddccced68a912a90049d95b972d0abe891b87cc43322b04845cd3510daf88bce550d0b016c89ab32b701012823bebc37017db06f1baee89d5d9dac30f0c4b39f08fc01ee110de51b9e35e47e30a6da003db29a97f33fcd3ef1e09eadef772895596d1d541edc05ec88f817ff31a531e157838e75d28a2cf51939f109c759027fa4d1137321bd4fbffae5bde52eb5bc3478ec9d96f955b94a46c2b3791a9c38e654afd3fb958c2f17113a293e6b5d205d8b6d1f37796c0f17193c90476478c491b4d2ccb70300effa9cae2b36e8cab7f84955a2993d8be8b14b9eb53d930c75b9630f78cc1b1242dadef044da43768d73be25ec81900b60d6f5c0cda9154956d3ceaffc22ccc027fa9c29c6433f81e5b4eac780184657154d486f9f168ea983d46fc746b9fc7c19d278a2d955f0fc4be8c4ca76ecc1cd2bd7ebdd3b49a1cc1b429c1e2c4e6fb4b3ab87abeb212a7bf969e7457719e78b6498143cf89705eb390ef321438bf43d281acff66cfd6d83ec93dc4e875b5415a12767c813fb35af40179689383a64b854a8143cf6404d9f3475a165a6237d5dce66604c547cbcfec58b57ab479b5d47a0f8f4d0889052423970a2d8d9c66bb4962e2681cefb0597ed7371c193cb90cdcf3d3462570569db701dc8f4c9bee08c649a062ee92c643d3d671dc644fc71b0e23a349d75559d7c2911b573d169322f7d8d8140d448a6344ace4c0873d859659760ec34f0d45f06fb37b9cffe1658410a71c29b435b9b41ee24c2496932c1e798f0fcd70f27e272d9904644660d79b4498d44dc5478bd9bc06622231725d082fd3d45deccdca5320a37e99290d886dac83c349c54726ccebb75cbbd841c4257152d28068eae6ef851638913e27e21016a110ac45d579b01f55796e537550d35f519052de21f88fdb4b16d8b26a171fe3cc4cec6be9058daa9efd193ac4b5f4883af4d857d52dc9eefaf71033c340a11f2ee688f9caa3f20ae53fd8eb420a2d87a8a878f674c0ada4f6acfc41abbf561d0a8a48a030bc754df924769f2e44b57b54e2321ce44261779bf70a386019fe341b0bc8ee4f8b91b602b1f59ae56c8c421f985a06be325b1c4d5bcb183ebb3faaab6d02d0270ac7f6423f4b684e0205f7b527e76b61929a9e2af7c6e4c237cf6e8572a007cd69c4fb4f32f3d0f618b49e3e0d6087058901d8af74936077f2935579f9573c94b704a8414219bd9613622d1c4278f8ce6b15616561d6729231d09efa84a0c8fb0b6dd62da8738f0e44ab644bef3fb311a929dcb46c8be89f64ba1574d3ef6437699f7e89d3fa79816b3df3dcee9b4ba5a264c0cda24db1e5c96e018e3fef477362bf717cecaca98c09a0eb83d405669ccb0e29dd5bbd9e1a94756bd3800b03c38eda1e7bb750f3df58b94363e3018d0f83efca7e6bb695334d44aead3fec66e488587fcb7d9ab92b941a0a26a66400482be50657a8884e8a21c04e7db267ef6d395fbc1659f6e5c540e9331cdc30bc3e1ef6d175dd3a9917f3d4f63f62e77578e6535ac01dfa0d27971c2901818b0f8d81f16ea9a30e2d0b1fa6d937870295284433210540db660e161b095f14d75b7366b76108cef1128a1bfa742cad6f61e76b625e7bae6043e5da3bf5ae1ca9cbbaf5906e6a57348467141a6bfda8c11ad0ab7c0255b9070f5bd8396740498f3569594564d04d0a4cc728ab634fdc1e92129f76c1c74e004d61e4a553d7c222ae2c6688afb35cbbd8e51217e1a222604c99b96869dd00821b41af9f4e6744820ec45fc40705ca41d269ff1e703d29c2055aa2e18194a994408d09e8661ae41992119df6dc6815ea98459748f9da5a8876f4f89bb427661652b471b57b54ee9b4b231b2d21c6b01e3f19cc345621ec27ab090a77ecaa94d4a65d622ec2eded6181960938f038e8564462e737583de8c21d3909a582b675759b46c7745054433c194e7e96b135026151496be4f7c2a6958d894be508d06ba281e74aade0830b8143e34eb097e3da29228c00e16f52d3119119956c0fbbe69f32408d4e01884215d8bef6565ce934bf173f27cb052a8ea3574121b003597f4d28a92f4ea3cb96b5f227184b19c68b2fdd4c8dee012eee5a2f5c8371cb310d11d6b8a8a372e205edf0c26f17a011ccfb016574497fbf5439eb32fd810c904fa8cff673b27857d7406fcbee57c0314baf235777e72f0008aec5896e8cabbffeccbfd5436e5379ca63ef6db29294a7a144678923c24ca424e650dc5069fdaa267fb5c751b07e4ac80a42d55910086614a58f2ea041efd991729919b80bc0a401527dd27ba8664986a9d20a0b25c470adeedff95cedc03e02cfcc75f9107c05eddde5c2476efc75b95cdf5d01f9deea21caef33b455437afa187efc702b1a63b8eeff42564ed93d29026ae864ff10f5889ee59c19f8add6ff0fb36540e3f743de7eaefc5020e6ce4e0c38b834456edaa7bf2dbc39d8e283a8cf45c78bd426396bae8debf213dd6268e72152900d81c61e981e0dd6519fb002ca8de45aa2a6f93b62a88015b4d38350311b13be3ccb6110024f7df8b927eddf7df17316522b2291d44512400ec0efc6028e64b8cf40340545f4028cc02a73039c10128a6146fa93282dde49497d2aed954971f811c6578aed0b07d79c893428c1bc443cabc8b650a6e8a02e2cd4fd7d77e0f86ff67620a042bc19a55ad93d29022e5c33ed8034690a280ec387ad2e6a4fc459b91f1baafa7239ad6ceb082efccc013998366867e0ed080f7eb76de661a3c764e841ea5f827f9d2a0c63fc75192ac2f9c5a05cad065fa51b9776e88000420b6bf24f54de710de1343a957cfd91ec1d39f917ac2c186a486e6f834d42859bae87da4de56d18a339d8d29e024832f07c7261b97de03dda4d7df839b8e51c101b67f87c10dc7af8c957e0c7555492b7f60fd33a1d1cc522a9aa2f8e133cbcec6ae804b394a0e1aadf11f51db4d523f3e2de984a1a70fb1cbf5770d27b685d0573e4437cd4b777eefdeb7c9d7360e0227447cfc6c2c1b93013b5283450fb9401ae9585fe0c72bd5af9e7131515127027b699b38df8596ed3648fcc8a2ad88ea1d6e142fe733ce1062d723d4867a3821195178b587fcbb32e62894b441fa222e090119da474fa5eb99acf49a8427efe7cd423889063e2a25f795fca77b916ca0b87d1ea1243b0f50526c06113914cec2ea252dd34c35a8760acc536dcd744c469a2621fe03869acf7a07bf76e1176d2d459db033c200053f37e9dc96c8002868c6bc1025e0fac56b1125bcb74872fef19f9089b4b4a71db05032e1dbc3166da26d81684e202f1a28c4c9b8f0d3166da26d81684e202f1a28c4c9b8f357dc0d1338e1405e2ad2f9e31cfc621019e7521b0c39dcc0d3ffec4afbc8d843e42090e6e31a4ea1c76feacc4fbae1a539dbd01d88816a43fd41d26efe922a746b55eda26d896672817a33ee0b27502a22037e69472172481054624c3e0f3375d45fc7cbed05bd7ee45ff3f2e74acd06ed32b058a5dde2cc8effb3ee1b38f18f977e091b38ea7dceed86c31e2b3775666a8875e46254130b49622af2a09af92151445b775f98a01e8f78cc45c7fd986cb53c60b6e9414984570c4f92818fce9cac8e767cd0911a9f7d367aa3417b162f1dd5601327b2e98491ccc012b5c022a6dce3133679c3ef677c7fa340c071ee528dc308f4d8160ec056061639fb4924726254cce34f7ec92f5d3672e5386a28930ee7034049f7c76c60d19165e032520f3d93ecf1baf5c4a92ab2348faf7fa31511d8a0ac8a03bb77e1eaa77dd1737c3799ff86073e567aba9dfcba6057ae4ad5594a5be7df143cc2542620793676e0538a38f0fcd62f580797c7c9ca53a298e2e621679629d18957c311efde789ae2b0ed5fada05890bd6de32d1bfe1b2f52235c5e778f3d8848a3482454a09e5cbfcdc86009c5cd0422a356965e22b8938a862457afa988062007ccdb2a21985a1248d4867e70880f37891669b9633db32bd309e02dc1f930b8e45d5bee01b469c63d196bc2bc784021faca5509a7d9e03bf71bf1028d6478d91f8f00ec6165c4812101fd346c9e21091780f424517afdd63f5a59a31dfcf3d9335bf78a5c2ef4b6a88465d7352bfeef311ad2e6cce7de4f0570306333beedec631edaeec128636b45a05ad0881033cd11ab561fca5620cb02b711c995bc7e6fc5eb3cf95370b502a68ef532af5679de9ad341af577e4b6b069033195d97fea5fc48c89cb2d0e0298023a493312ebd422469d29f4d852b0b65f004c4ee05db9197355e138a182a4c5428693b9d3a5040d069a2d5662a1722e129ba46548ae951dab8cb88f3ff4c74849664f8e860a88081b1f57f8fe6b36aa3c2ec69ca5e48c67923a86c31ac4c9ce27a42c9c53df6d3f6bfb83c6a8a91a2a9d6c74786b4f1c8ce7fc07e7f5bbb43ef4b5b16b9bde20fcdedc904ea9e26131d51b749df37b1df4c212bd957350c24edd8a98e3a777ea608966969856edebf9c0bef8b642d5579b14df2af710fc5c91b997917a659303ce695dce6dbad7d6bdd8f2878a625db8cb6542f621ce3f1f9a9bcca2a1877ad558b172236685a6d0ba4dd8353922f3efb31cb6f802b89237adb02832b2f030efa9587f8bf317126a5b8361aa26dd2341007ccdb2db2c66e45caf05917071c38c29c2e5e529eac8eecb5b94bbc327c89df295c931ca735076822cf0d353c17cbf26cfc88dc2e7f94147009ecfcd0bac9c7dae676b19c10836325ab419ca0c79f951982e01372922542971b598b405e31722969a4b8a6d6e72e1f9cafb36f19f7c76e62c2e9d47b17038c2bb97b1b075cd40a9661ffe61ca2098027abc7c28049212fb5cf6a90273f695cd4e34fae1e7ed50da933ad7c158abe7fb4bc5297073cfb963400dd90de24c52d488eb590480707493295fde769030785cf79da63896cd27cfe8d4298685a366c60e21e9261588223ff17629eedaa49033f5b411b7f7cd6c5eb90dd098286fe809e7e755b9e1c125c8febe295e193f3a048456656c0dd5704a6dd9e5b3d6f1740995b7d5877d6f7b9b1937d535ef7bb67d7d213595eaf901f2696b0c125daacd7a8c939351dd038edafd49aa30439de55ebc39110d80be33daedaaf222dd9fe7fb0927a7bdf84cfe1770fa36f06179fc9d1db037eb58bdf1588e68104d4274b0b5507fff1fdefd5eb71038b5f73e297fa06e4128d39d1f93e4ce0dd2350e1e19f0e4046bce0d19b9e13c438067e4b0b843e01ae4c97cfb7d0a1a927216321e9b3a10b471f176d9a64d6a767f65703fa9c04db879e9736503e6ce06c8386dd70554f8003873686487e0dbb11869c87571868b8ed14ca691038f7edebc04077adb8436ac033b5fc6203afbf429253a64895594a5e9a97e099949f44e78082dcc1f0cb042f7b2eedfb48b99472ef113336d394c15f8e458d7253041f4c6731ad552b27c23946adbda11aca596de20308303e2a4f0b3f7306d5159388f522d9a74b5b2735cdc1c9e1c80280f3b40a7929073d01bf3d0f579fb3616a3f26384a79b19f866cb8c8d5edcd05377c36696165200591ea6ca67acb9546b9c345a3d0b066dc5f18d2b146770371faa5d8e7e963c20afa1dd81328979f59c095dd6be88d476320f271dde189a96afa586c5155ac475d98dce71b3bf024e0f7496d63010cbce4aaad915b04cb43c1818ac31e0b275c263ad02d234f002e70e290cc16d6253ad74d62109baedd96e8cabcf84228240b4d81738acf776ef76919b9b28ce469ce169838b45ed64e68a79cff3bc84687c90d50a0c14c3f4b071245722d969d236a72e869221f8ec6fe302862b7c9bb82860e43a15d59d4966429374f47b97b4f2675abef8a39755711a2d65a7e63c1fb9c5487d76be8c80225119bbd9da791f39bca85b9509ef43b03b9a8dc77ab902156b48afd3ed737ef111fe9939a94eed9915f45ae322630bf831909e8f65a6676e259b547bfffbe86fe3d2290fb0c01bb426d2392d0f107c6af034cdf3aa0f6d74b3420ef6bbe7960c905b880c0f10942c5324a27e955fdd5fb3c5e16379e659b3803d4405ce0cdf7e7f6234ce34cecdf07534902182c8ee3d38d3bf34b74c5a7c793df3dc3ecd871d2eddfaee7278818a03e26cf031f27b6beb98b122766fbd2be1d09c65ceecaf7d77ec85744d85936cc7b188024bf1c7c9cd4e1802df3835713b8461ee76b4f85e595d39060df016277d139dbc0544cd4fcb0522d617321d8852911d5631f277bda9db6e9d40a7bde1dbb9ee43bad83abb373e35f8963113306687490e874589f8749df08fdea0650e2c0470b21dc57ed4fe103733426150cd4ebcdb3aa620761de19afb4f9257929055c1f4da5a40e919154bbb0b0bcae6cbd0d09f37cecc2e5f35e18c54b0e7c2e489b40ef24f3fe7ccd61afb528f68afe1db4d9cb9110b0fc58dfc8d329fa336ebd964d023153a467bb3f654e23f9411b000e16f4eaecb0e1f4d090e9e9f8b97b174e52a059f5dd183135c377af379df51ba9d82fb31a9cb957e81db9b1b73ac80ba58964dce0e820028fabd337a31ea12f202cc43f57b6e667008726d45900a222a50269049498823b4face98f17e6ccb6e345723d4180d69be7f089548ef5e0f8cfdcdc288afa1a9d9c8f68ed50478e04aaff087aaa82daf4381b5c36ee44a6fea3f99b6362d11ea68c203443b27d2dbf3f12e186254c1c6ec563909dc19e4cb1cf937786c7e2856a4817eab5737c8d8ffc25156465e2926bb5275e2ca4e2ba0b17db31ccc8ca424a8f5aa1ec3ef2a249314a68af4019745b832b6eb66335891634c52b336ef0f9b4e2ad33d6d84a680879cae728d3eb331179c1baee4fe333d7d7a9731a60fc7c56d532c9b46e9061de3cc1f9611f37ef56a8af72b4f189fca6e701cb953c9c8e3f199b36b269297f87e7d980fa5fac6c91f44076c9ca40bae5280dcbadcd5397169c77a09c291127956a61c7109839f6530d43a01c3c9d710491d1eb1b0ea3b01a9798c6bc2194210925147dd47f078e101ab395f7f63cfd4325f603731f17705d394b33b1679542623ca9ebf1af36cdb3dab7925f81f093e99f046055922a5024699bd0f20ef6937afa01e6ecec4b3cdb224fe0bd7b53f199ccdd2b8fdd9761da568b58acba083a596180e51f8615a867d808f17c31b4623a0412712925bb2a7236740f18187a98a1ab3b7e1918344c6caec9d2d32f983aba579ccde076858bcf3dc9521d16151394461ff7345c3bffd926c56d869a4876183650f385e0705402ac3e4548d6ae9ec01312520a022da59d5d63d0f5f6a4b6d6c8e402f8a5f5789984ac2ac0548a76e8c69ec5589e9ec7d7fa25715ca2d6ed869e18192777f30b539a0a37b16403478298c436ccc72fe7cb8d2ff2de0758b06542be4cbef2ba70559c91d3766f110cfe3f9ead656147ce1919800964b2c6528d209a3b02da57ff06bacd163be4972b5b4a6f8d757a42aa8cc0522d6173319980aea244fced2fae9a39738c8b5f192d557b678167274314f138b139dd6ff7a505e1534b36d8cd2abeac5eeef33d40d2faaebec0cb7e860509d5b5a10cf28af6e1c4ed281f0afe247f703e1ed6e89d4472e1e39ad34d857d555db1ca97493153f9de0604e0131771e3b98642fb68c96939dbd7fc149961a98ff3a2332a5d7dbe61df43a55f0f27861ea002797de20301825a59b59ae734ae6cc1a1867ad1f850f26e2ac2c7fa0ce0a62bef3f350b79433af287e4e326341f4105a3b5887124a87522b715643cad17d01a4a17842e3cd262ffe47c22ffe028d8be48174c522816fea0b01835439f8ec3780e8ac641bb6548bb5173ff38976b2a6738adb3924996c1e8735fee6188ac7583f5d8c3a8c6be7f49b5a46e92ee50b0b25e178607ea1ed5d41bff3457777ec83fd99165b777d836a3f2bebbe78f4b63744d9b6889eabe616e9c25feb29a5da331c7767614228a90f87752c18a0d992458208b8658a8dab6e438da26619a91644b349e2a20b6f8f6c6e02c1f30a03c38e2548f82fcdb790153efed1416757ac2fc2e5f1963720a4ae408e0149849408eb35f0ecff8ee54a140227119113b326ba7f4c1cc126ad9d18b997a01307be9d018a9b434a2b275e187c961ce6184aa67ad704d6f86ead8f0ca7c51ce7b4dc2ce0bafa719d7ad95fd938427fff9d8a140b25d9e06ff27bf861bc103dbd4fe062ae137ae9f314371b997826e386add0a41907d9b1a20508098f6e60685d05ba9fe02b2b5c01dae7ae8caaaa6294cf6d2ac5c610dcfa885de35aa5e444d889c2a3dea6e2a4019a5de35aa504f4c59ab9be0fa222b58619ae6499fc86754b4e199c0ca2a305c92605c359b8763aba7820af236a3a49b7947f89efe35e8f2cff790a846dc2a8078988ae6b013df74caa262454b4e2a401dde32ced205fb4a6b8c3402f3748dabe454b6d68bb745b0f6cb0824984710570f2f3446878c61ee63ae74eb7ba0dd23adae7cee0f1dcc43af8f4e66b82812ff2d5ccea6f27efc008fe65c96701dee023ab074cca6f9e7c4036b93baf4b6cf838dffcdb442ad93a271ece9fb4e28a3e63243e691e87728c56faa6d06592e85e830c0418e93ef8d7c71a0b875199b64321c7e40aa276d26fde042867a7738a2022d8cccf8362ac76b8e0980c925fc0ad9aa88c38a862f971b1325cde52a7a70740d8bfc1cb9d5c4a65b33f1d6b82a5bc6681eacb72509dbd8642ce8405ec4b3915844c9b683b30658273ff3d947c5ba57790636bee8fa92afc34b93bdfd6e2a4011da27b42a9627ad4013458a34352fdf4f36a89efe33e8fccf2ff1ca251dbf91f55a0a8e08775843a19ac080b25e1766c6fbde31a1f495ac419ac080b25d93aababdf522e9f79d6032428c11325e1b8620fcf4f0e08b2ef65a78b1e806062652192c35c143e23ef82065ce3f43543e710562861f819776beb29908bca97b105307ad6ffcbedc002746c194ea85377c38a7b585e5f82dc1041aa2624d2df30f52ccbe85b87cbe9c4448ec4202f31e4a4019dca93080c708b0bc978f865c8d959c143abe834d51bee9a2691179be21f09c3996547c2abee896d5a2452a6347bd09d795a8c0eef61b435f781fea1b4e8b4094e624441cb53570ccf43e3628b0c69c7dae7ae9c1ff93f5a1c8297c462f5ae1406620eafdf0129a90b2152c6b460a7015a04ea16bb6bcbfc1121832d734a7123d86889efffa0ec9e24d4b2a6734a7123d86889ef77e9b1c9e36e3369f8141b8aceedcb4ee624008aa2c2620c' ``` ```python from pwn import xor # .png files all have headers like this. This is the first block of plaintext png_header = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR' #this is the first block of the ciphertext b1=bytes.fromhex(ct[:32]) key=xor(png_header,b1) #now we use the key to xor with the whole png header data val=xor(key,ct) with open('bean.png','wb') as f: f.write(bytes(val)) ``` # RSA ## ****`PUBLIC EXPONENT`**** ### `> Crossed Wires` ```python ''' Here we are provided with multiple public key exponent i.e "e". Therefore, we will have have mutiple d with respective to different e values ''' import binascii import gmpy2 n=21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771 p=134460556242811604004061671529264401215233974442536870999694816691450423689575549530215841622090861571494882591368883283016107051686642467260643894947947473532769025695530343815260424314855023688439603651834585971233941772580950216838838690315383700689885536546289584980534945897919914730948196240662991266027 q=161469718942256895682124261315253003309512855995894840701317251772156087404025170146631429756064534716206164807382734456438092732743677793224010769460318383691408352089793973150914149255603969984103815563896440419666191368964699279209687091969164697704779792586727943470780308857107052647197945528236341228473 print("n==p*q?",n==p*q) n_e_values=[(21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771, 106979), (21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771, 108533), (21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771, 69557), (21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771, 97117), (21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771, 103231)] phi=(p-1)*(q-1) #writing a loop to find all the d values c = 20304610279578186738172766224224793119885071262464464448863461184092225736054747976985179673905441502689126216282897704508745403799054734121583968853999791604281615154100736259131453424385364324630229671185343778172807262640709301838274824603101692485662726226902121105591137437331463201881264245562214012160875177167442010952439360623396658974413900469093836794752270399520074596329058725874834082188697377597949405779039139194196065364426213208345461407030771089787529200057105746584493554722790592530472869581310117300343461207750821737840042745530876391793484035024644475535353227851321505537398888106855012746117 for e_values in n_e_values: d=gmpy2.invert(e_values[1],phi) c=pow(c,d,n) # Note: c has to be updated every iteration print(binascii.unhexlify(hex(c)[2:])) ``` ### `> Infinite Descent` ```python import binascii import gmpy2 n = 383347712330877040452238619329524841763392526146840572232926924642094891453979246383798913394114305368360426867021623649667024217266529000859703542590316063318592391925062014229671423777796679798747131250552455356061834719512365575593221216339005132464338847195248627639623487124025890693416305788160905762011825079336880567461033322240015771102929696350161937950387427696385850443727777996483584464610046380722736790790188061964311222153985614287276995741553706506834906746892708903948496564047090014307484054609862129530262108669567834726352078060081889712109412073731026030466300060341737504223822014714056413752165841749368159510588178604096191956750941078391415634472219765129561622344109769892244712668402761549412177892054051266761597330660545704317210567759828757156904778495608968785747998059857467440128156068391746919684258227682866083662345263659558066864109212457286114506228470930775092735385388316268663664139056183180238043386636254075940621543717531670995823417070666005930452836389812129462051771646048498397195157405386923446893886593048680984896989809135802276892911038588008701926729269812453226891776546037663583893625479252643042517196958990266376741676514631089466493864064316127648074609662749196545969926051 e = 65537 c = 98280456757136766244944891987028935843441533415613592591358482906016439563076150526116369842213103333480506705993633901994107281890187248495507270868621384652207697607019899166492132408348789252555196428608661320671877412710489782358282011364127799563335562917707783563681920786994453004763755404510541574502176243896756839917991848428091594919111448023948527766368304503100650379914153058191140072528095898576018893829830104362124927140555107994114143042266758709328068902664037870075742542194318059191313468675939426810988239079424823495317464035252325521917592045198152643533223015952702649249494753395100973534541766285551891859649320371178562200252228779395393974169736998523394598517174182142007480526603025578004665936854657294541338697513521007818552254811797566860763442604365744596444735991732790926343720102293453429936734206246109968817158815749927063561835274636195149702317415680401987150336994583752062565237605953153790371155918439941193401473271753038180560129784192800351649724465553733201451581525173536731674524145027931923204961274369826379325051601238308635192540223484055096203293400419816024111797903442864181965959247745006822690967920957905188441550106930799896292835287867403979631824085790047851383294389 p = 19579267410474709598749314750954211170621862561006233612440352022286786882372619130071639824109783540564512429081674132336811972404563957025465034025781206466631730784516337210291334356396471732168742739790464109881039219452504456611589154349427303832789968502204300316585544080003423669120186095188478480761108168299370326928127888786819392372477069515318179751702985809024210164243409544692708684215042226932081052831028570060308963093217622183111643335692361019897449265402290540025790581589980867847884281862216603571536255382298035337865885153328169634178323279004749915197270120323340416965014136429743252761521 q = 19579267410474709598749314750954211170621862561006233612440352022286786882372619130071639824109783540564512429081674132336811972404563957025465034025781206466631730784516337210291334356396471732168742739790464109881039219452504456611589154349427303832789968502204300316585544080003423669120186095188478480761108168299370326928127888786819392372477069515318179751702985809024210164243409544692708684215042226932081052831028570060308963093217622183111643335692362635203582868526178838018946986792656819885261069890315500550802303622551029821058459163702751893798676443415681144429096989664473705850619792495553724950931 phi=(p-1)*(q-1) d=gmpy2.invert(e,phi) m = pow(c,d,n) print("plain text: \n",binascii.unhexlify(hex(m)[2:])) ''' plain text: b'crypto{f3rm47_w45_4_g3n1u5}' ''' ``` ### `> Marin's Secret` ```python ''' We will Directly Decompose N ''' from Crypto.Util.number import inverse import binascii n = 658416274830184544125027519921443515789888264156074733099244040126213682497714032798116399288176502462829255784525977722903018714434309698108208388664768262754316426220651576623731617882923164117579624827261244506084274371250277849351631679441171018418018498039996472549893150577189302871520311715179730714312181456245097848491669795997289830612988058523968384808822828370900198489249243399165125219244753790779764466236965135793576516193213175061401667388622228362042717054014679032953441034021506856017081062617572351195418505899388715709795992029559042119783423597324707100694064675909238717573058764118893225111602703838080618565401139902143069901117174204252871948846864436771808616432457102844534843857198735242005309073939051433790946726672234643259349535186268571629077937597838801337973092285608744209951533199868228040004432132597073390363357892379997655878857696334892216345070227646749851381208554044940444182864026513709449823489593439017366358869648168238735087593808344484365136284219725233811605331815007424582890821887260682886632543613109252862114326372077785369292570900594814481097443781269562647303671428895764224084402259605109600363098950091998891375812839523613295667253813978434879172781217285652895469194181218343078754501694746598738215243769747956572555989594598180639098344891175879455994652382137038240166358066403475457 e = 65537 c = 400280463088930432319280359115194977582517363610532464295210669530407870753439127455401384569705425621445943992963380983084917385428631223046908837804126399345875252917090184158440305503817193246288672986488987883177380307377025079266030262650932575205141853413302558460364242355531272967481409414783634558791175827816540767545944534238189079030192843288596934979693517964655661507346729751987928147021620165009965051933278913952899114253301044747587310830419190623282578931589587504555005361571572561916866063458812965314474160499067525067495140150092119620928363007467390920130717521169105167963364154636472055084012592138570354390246779276003156184676298710746583104700516466091034510765027167956117869051938116457370384737440965109619578227422049806566060571831017610877072484262724789571076529586427405780121096546942812322324807145137017942266863534989082115189065560011841150908380937354301243153206428896320576609904361937035263985348984794208198892615898907005955403529470847124269512316191753950203794578656029324506688293446571598506042198219080325747328636232040936761788558421528960279832802127562115852304946867628316502959562274485483867481731149338209009753229463924855930103271197831370982488703456463385914801246828662212622006947380115549529820197355738525329885232170215757585685484402344437894981555179129287164971002033759724456 p = 1475979915214180235084898622737381736312066145333169775147771216478570297878078949377407337049389289382748507531496480477281264838760259191814463365330269540496961201113430156902396093989090226259326935025281409614983499388222831448598601834318536230923772641390209490231836446899608210795482963763094236630945410832793769905399982457186322944729636418890623372171723742105636440368218459649632948538696905872650486914434637457507280441823676813517852099348660847172579408422316678097670224011990280170474894487426924742108823536808485072502240519452587542875349976558572670229633962575212637477897785501552646522609988869914013540483809865681250419497686697771007 q = 446087557183758429571151706402101809886208632412859901111991219963404685792820473369112545269003989026153245931124316702395758705693679364790903497461147071065254193353938124978226307947312410798874869040070279328428810311754844108094878252494866760969586998128982645877596028979171536962503068429617331702184750324583009171832104916050157628886606372145501702225925125224076829605427173573964812995250569412480720738476855293681666712844831190877620606786663862190240118570736831901886479225810414714078935386562497968178729127629594924411960961386713946279899275006954917139758796061223803393537381034666494402951052059047968693255388647930440925104186817009640171764133172418132836351 phi = (p - 1) * (q - 1) d = inverse(e, phi) pt=pow(c,d,n) print("plain text: \n",binascii.unhexlify(hex(pt)[2:])) ''' plain text: b'crypto{Th3se_Pr1m3s_4r3_t00_r4r3}' ''' ``` ### `> Fast Primes` <aside> 💡 In this challenge the ciphertext is encrypted based on RSA and the OAEP padding. To decrypt we follow the same general process, until we reach the final step where we will use resources from PKCS#1 OAEP python Documentation. </aside> [PKCS#1 OAEP (RSA) - PyCryptodome 3.15.0 documentation](https://pycryptodome.readthedocs.io/en/latest/src/cipher/oaep.html) To view the `key.pem` file ```python from Crypto.PublicKey import RSA f = open('key.pem','r') key = RSA.import_key(f.read()) #Get the n and e value n=key.n e=key.e ``` **Final Solution** ```python import math from Crypto.Cipher import PKCS1_OAEP from Crypto.PublicKey import RSA from Crypto.Util.number import * import gmpy2 import binascii e = 0x10001 p=51894141255108267693828471848483688186015845988173648228318286999011443419469 q=77342270837753916396402614215980760127245056504361515489809293852222206596161 n = p * q phi = (p - 1) * (q - 1) d=gmpy2.invert(e,phi) #Note: don't forget to change the datatype of d from mpz to int d=int(d) key = RSA.construct((n, e, d)) cipher = PKCS1_OAEP.new(key) ct="249d72cd1d287b1a15a3881f2bff5788bc4bf62c789f2df44d88aae805b54c9a94b8944c0ba798f70062b66160fee312b98879f1dd5d17b33095feb3c5830d28" ct=binascii.unhexlify(ct) pt = cipher.decrypt(ct) print("Plaintext: \n",pt) ''' Plaintext: b'crypto{p00R_3570n14}' ''' ``` ### `> Signing Server` ## Source code ```python #!/usr/bin/env python3 from Crypto.Util.number import bytes_to_long, long_to_bytes from utils import listener class Challenge(): def __init__(self): self.before_input = "Welcome to my signing server. You can get_pubkey, get_secret, or sign.\n" def challenge(self, your_input): if not 'option' in your_input: return {"error": "You must send an option to this server"} elif your_input['option'] == 'get_pubkey': return {"N": hex(N), "e": hex(E) } elif your_input['option'] == 'get_secret': secret = bytes_to_long(SECRET_MESSAGE) return {"secret": hex(pow(secret, E, N)) } elif your_input['option'] == 'sign': msg = int(your_input['msg'], 16) return {"signature": hex(pow(msg, D, N)) } else: return {"error": "Invalid option"} listener.start_server(port=13374) ``` ## Solution ```python from pwn import * import json s=remote('socket.cryptohack.org','13374') e_n_value=json.dumps({"option": "get_pubkey",}).encode() ct=json.dumps({"option": "get_secret",}).encode() print(s.recvline().decode()) s.sendline(ct) secret=eval(s.recvline().decode()) ciphertext=secret['secret'] flag=json.dumps({"option":"sign","msg":str(ciphertext)}).encode() s.sendline(flag) print(s.recvline().decode()) ''' Output: TODO: audit signing server to make sure that meddling hacker doesn't get hold of my secret flag: crypto{d0n7_516n_ju57_4ny7h1n6} ''' ``` # ****DIFFIE-HELLMAN**** ## Group Theory ### `> Additive` <aside> 💡 In the additive group of Fp the discrete logarithms are simply the inverses! If a, b are the `secret keys` of Alice and Bob, we have that `A≡a⋅g(modp)` and `B≡b⋅g(modp)`, where A,B are the public keys. Hence we can recover the shared secret `s≡a⋅b⋅g(modp)` simply by recovering `a≡A⋅g−1(modp)`. </aside> ```python from pwn import * from Crypto.Cipher import AES import hashlib import json s=remote('socket.cryptohack.org', '13373') #decryption from DH starter 5 challenge def decrypt_flag(shared_secret: int, iv: str, ciphertext: str): # Derive AES key from shared secret sha1 = hashlib.sha1() sha1.update(str(shared_secret).encode('ascii')) key = sha1.digest()[:16] # Decrypt flag ciphertext = bytes.fromhex(ciphertext) iv = bytes.fromhex(iv) cipher = AES.new(key, AES.MODE_CBC, iv) plaintext = cipher.decrypt(ciphertext) return plaintext alice=s.recvline().decode() alice=eval(alice[24:]) p=int(alice["p"],16) g=int(alice["g"],16) A=int(alice["A"],16) # Finding a value a = A * pow(g, -1, p) bob=s.recvline().decode() bob=eval(bob[22:]) B=int(bob["B"],16) shared_secret = (a * B) % p print(f"shared_secret:{shared_secret}") aes_value=s.recvline().decode() aes_value=eval(aes_value[24:]) print(aes_value) iv=aes_value["iv"] ciphertext=aes_value["encrypted"] shared_secret = 781375771286523921682177536065612640494056443200091700967335945135067268298653748996108322420480852844610393628365669516985078401109539826459162980506903623821748901865082257455849415791691881709448350504513673033914283456716068601658189110168981077531264035904750408276794096099693262398008324260587814780721089721047526118391598923434827194939854735798577674170203256314333942485760352085036751905369610326318228197468392759214066509014997981777945415195218141 iv = "c396c71ca5eef7b2b03f359e30de5924" ciphertext = "5fcf13b8d7e15aed2ac21bbe0ab732e9e75b221a4cba4c90cd63a25d0dda2461d24bc10e7dceffc0fe5a9b4ae00bfaa7" print(decrypt_flag(shared_secret, iv, ciphertext)) ``` > Note: There was some issue with my script… manual decryption works fine. >