├── .gitignore ├── README.md ├── pa6 ├── challenges.py └── factor.py ├── pa3 └── streamhash.py ├── pa5 └── discrete_log.py ├── pa1 ├── ct.py └── decrypt.py ├── ps1 └── solutions.txt ├── pa4 └── po_attack.py ├── ps2 └── solutions.txt ├── final └── solutions.txt ├── ps6 └── solutions.txt ├── ps3 └── solutions.txt ├── pa2 └── bc_modes.py ├── ps5 └── solutions.txt └── ps4 └── solutions.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *~ 3 | *.swp 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | crypto1 2 | ======= 3 | 4 | Coursera Stanford Cryptography 1 5 | 6 | 7 | These are my problem set and programming assignment solutions for the Spring 2014 term. 8 | 9 | 10 | ##NOTICE 11 | These solutions are for reference only and should not be viewed by students currently enrolled in the course. Students who view solutions to homework and programming assignments that they have not yet completed deprive themselves of learning and understanding. 12 | 13 | **If you read these solutions before completing the course, you will diminish the course's value for yourself and others.** 14 | -------------------------------------------------------------------------------- /pa6/challenges.py: -------------------------------------------------------------------------------- 1 | N_1 = 179769313486231590772930519078902473361797697894230657273430081157732675805505620686985379449212982959585501387537164015710139858647833778606925583497541085196591615128057575940752635007475935288710823649949940771895617054361149474865046711015101563940680527540071584560878577663743040086340742855278549092581 2 | 3 | N_2 = 648455842808071669662824265346772278726343720706976263060439070378797308618081116462714015276061417569195587321840254520655424906719892428844841839353281972988531310511738648965962582821502504990264452100885281673303711142296421027840289307657458645233683357077834689715838646088239640236866252211790085787877 4 | 5 | N_3 = 720062263747350425279564435525583738338084451473999841826653057981916355690188337790423408664187663938485175264994017897083524079135686877441155132015188279331812309091996246361896836573643119174094961348524639707885238799396839230364676670221627018353299443241192173812729276147530748597302192751375739387929 6 | 7 | ciphertext_1 = 22096451867410381776306561134883418017410069787892831071731839143676135600120538004282329650473509424343946219751512256465839967942889460764542040581564748988013734864120452325229320176487916666402997509188729971690526083222067771600019329260870009579993724077458967773697817571267229951148662959627934791540 8 | 9 | e_1 = 65537 10 | 11 | Ns = (N_1, N_2, N_3) 12 | -------------------------------------------------------------------------------- /pa3/streamhash.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | import os 3 | import mmap 4 | import binascii 5 | 6 | from Crypto.Hash.SHA256 import SHA256Hash 7 | 8 | class StreamHash(object): 9 | def __init__(self, hashClass=None): 10 | if not hashClass: 11 | hashClass = SHA256Hash 12 | 13 | self._hashClass = hashClass 14 | self._blockSZ = 1024 15 | 16 | def hash(self, filePath): 17 | with open(filePath, "rb") as f: 18 | with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm: 19 | return self._hash_bytes(mm) 20 | 21 | def _hash_bytes(self, fileBytes): 22 | blocks, lastSZ = divmod(len(fileBytes), self._blockSZ) 23 | 24 | start = len(fileBytes) - lastSZ 25 | end = len(fileBytes) 26 | 27 | currHash = bytes() 28 | while start >= 0: 29 | block = fileBytes[start:end] + currHash 30 | hashObj = self._hashClass() 31 | hashObj.update(block) 32 | currHash = hashObj.digest() 33 | 34 | end = start 35 | start -= self._blockSZ 36 | 37 | return currHash 38 | 39 | 40 | def self_test(testsPath): 41 | from os import listdir 42 | for f in listdir(testsPath): 43 | # Verify that the name matches the hash 44 | try: 45 | sh = StreamHash() 46 | hashVal = sh.hash(os.path.join(testsPath, f)) 47 | hashHex = binascii.hexlify(hashVal).decode() 48 | if hashHex == f: 49 | print("MATCH {}".format(f)) 50 | else: 51 | print("NO MATCH {} != {}".format(f, hashHex)) 52 | except Exception as e: 53 | print("ERROR {} -> {}".format(f, e)) 54 | 55 | 56 | if __name__ == "__main__": 57 | import sys 58 | if len(sys.argv) > 1: 59 | sh = StreamHash() 60 | print(binascii.hexlify(sh.hash(sys.argv[1])).decode()) 61 | else: 62 | self_test("files") 63 | 64 | -------------------------------------------------------------------------------- /pa5/discrete_log.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys 4 | import math 5 | import gmpy2 6 | 7 | from gmpy2 import mpz 8 | from gmpy2 import invert 9 | from gmpy2 import powmod 10 | from gmpy2 import divm 11 | 12 | def compute_x0s(p,h,g,B): 13 | return ((i, powmod(g, B*i, p)) for i in range(B)) 14 | 15 | def discrete_log(p, h, g, maxExp=40): 16 | """ Computes x such that h = g^x mod p 17 | """ 18 | 19 | B = mpz(2**(int(maxExp/2))) 20 | 21 | g = mpz(g) 22 | h = mpz(h) 23 | p = mpz(p) 24 | 25 | print("Computing x1s...") 26 | x1s = { divm(h, powmod(g,i,p), p) : i for i in range(B) } 27 | 28 | print("Checking for equality...") 29 | for x0, exprR in compute_x0s(p,h,g,B): 30 | x1 = x1s.get(exprR) 31 | if x1 is not None: 32 | print("Found values!") 33 | print("x0 = {}".format(x0)) 34 | print("x1 = {}".format(x1)) 35 | return mpz(x0)*B+mpz(x1) 36 | 37 | raise ValueError("No suitable x0, x1 found!") 38 | 39 | def self_test(): 40 | p = 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084171 41 | 42 | g = 11717829880366207009516117596335367088558084999998952205599979459063929499736583746670572176471460312928594829675428279466566527115212748467589894601965568 43 | 44 | h = 3239475104050450443565264378728065788649097520952449527834792452971981976143292558073856937958553180532878928001494706097394108577585732452307673444020333 45 | 46 | print("Running tiny test") 47 | xTiny = 3 48 | x = discrete_log(97, 20, 57, 6) 49 | print("x == {}".format(x)) 50 | assert(xTiny == x) 51 | print("Tiny test passed!") 52 | print("") 53 | 54 | print("Running short test") 55 | xShort = 23232 56 | x = discrete_log(1938281, 190942, 1737373, 16) 57 | print("x == {}".format(x)) 58 | assert(xShort == x) 59 | print("Short test passed!") 60 | print("") 61 | 62 | print("Running long test") 63 | x = discrete_log(p, h, g, 40) 64 | assert(h == powmod(g,x,p)) 65 | print("x == {}".format(x)) 66 | print("Long test passed!") 67 | print("") 68 | 69 | if __name__ == "__main__": 70 | self_test() 71 | 72 | -------------------------------------------------------------------------------- /pa6/factor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import gmpy2 4 | 5 | from gmpy2 import mpz 6 | from gmpy2 import powmod 7 | from gmpy2 import isqrt 8 | from gmpy2 import isqrt_rem 9 | from gmpy2 import div 10 | from gmpy2 import invert 11 | 12 | from math import ceil 13 | from binascii import unhexlify 14 | 15 | import challenges 16 | 17 | def ceil_sqrt(x): 18 | s,t = isqrt_rem(x) 19 | return s + (1 if t else 0) 20 | 21 | def check_factors(p,q,N): 22 | return p*q == N 23 | 24 | def factor_with_average(A, N): 25 | x = isqrt(A**2 - N) 26 | return (A - x, A + x) 27 | 28 | def check_ch3(i,A,N): 29 | p,q = (div(A + i - 1,3), div(A - i,2)) 30 | if check_factors(p,q,N): 31 | return p,q 32 | 33 | p,q = (div(A - i,3), div(A + i - 1,2)) 34 | if check_factors(p,q,N): 35 | return p,q 36 | 37 | return None 38 | 39 | 40 | def ch3_factor(N): 41 | """ Valid when |3p - 2q| < N^(1/4) 42 | """ 43 | 44 | A = ceil_sqrt(6*N) 45 | 46 | # let M = (3p+2q)/2 47 | # M is not an integer since 3p + 2q is odd 48 | # So there is some integer A = M + 0.5 and some integer i such that 49 | # 3p = M + i - 0.5 = A + i - 1 50 | # and 51 | # 2q = M - i + 0.5 = A - i 52 | # 53 | # N = pq = (A-i)(A+i-1)/6 = (A^2 - i^2 - A + i)/6 54 | # So 6N = A^2 - i^2 - A + i 55 | # i^2 - i = A^2 - A - 6N 56 | 57 | # Solve using the quadratic equation! 58 | a = mpz(1) 59 | b = mpz(-1) 60 | c = -(A**2 - A - 6*N) 61 | 62 | det = b**2 - 4*a*c 63 | 64 | roots = (div(-b + isqrt(b**2 - 4*a*c), 2*a), 65 | div(-b - isqrt(b**2 - 4*a*c), 2*a)) 66 | 67 | 68 | for i in roots: 69 | if i >= 0: 70 | f = check_ch3(i,A,N) 71 | if f: 72 | return f 73 | 74 | # We should have found the root 75 | assert(False) 76 | 77 | def factor(N): 78 | N = mpz(N) 79 | 80 | # Valid when |p-q| < 2N^(1/4) 81 | A = ceil_sqrt(N) 82 | p,q = factor_with_average(A, N) 83 | if check_factors(p,q,N): 84 | return (p,q) 85 | 86 | # Valid when |p-q| < 2^11 * N^(1/4) 87 | for i in range(2**20): 88 | A += 1 89 | p,q = factor_with_average(A, N) 90 | if check_factors(p,q,N): 91 | return (p,q) 92 | 93 | return ch3_factor(N) 94 | 95 | def decrypt_RSA(ciphertext, pk): 96 | N, e = pk 97 | 98 | p,q = factor(N) 99 | phiN = N - p - q + 1 100 | 101 | d = invert(e, phiN) 102 | 103 | return powmod(ciphertext, d, N) 104 | 105 | 106 | def self_test(): 107 | Ns = challenges.Ns 108 | 109 | for num,N in enumerate(Ns): 110 | p,q = factor(N) 111 | 112 | if check_factors(p,q,N): 113 | print("N[{}]: Found p = \n{}\n".format(num, min(p,q))) 114 | else: 115 | print("ERROR: Incorrectly factored N[{}]!".format(num)) 116 | 117 | # Find the plaintext 118 | pt = decrypt_RSA(challenges.ciphertext_1, (challenges.N_1, challenges.e_1)) 119 | ptHex = hex(pt) 120 | pos = ptHex.find("00") 121 | print("Plaintext:") 122 | print(ptHex) 123 | print("Message:") 124 | print(unhexlify(ptHex[pos+2:])) 125 | 126 | if __name__ == "__main__": 127 | self_test() 128 | 129 | 130 | -------------------------------------------------------------------------------- /pa1/ct.py: -------------------------------------------------------------------------------- 1 | 2 | ctshex = [ 3 | "315c4eeaa8b5f8aaf9174145bf43e1784b8fa00dc71d885a804e5ee9fa40b16349c146fb778cdf2d3aff021dfff5b403b510d0d0455468aeb98622b137dae857553ccd8883a7bc37520e06e515d22c954eba5025b8cc57ee59418ce7dc6bc41556bdb36bbca3e8774301fbcaa3b83b220809560987815f65286764703de0f3d524400a19b159610b11ef3e", 4 | 5 | "234c02ecbbfbafa3ed18510abd11fa724fcda2018a1a8342cf064bbde548b12b07df44ba7191d9606ef4081ffde5ad46a5069d9f7f543bedb9c861bf29c7e205132eda9382b0bc2c5c4b45f919cf3a9f1cb74151f6d551f4480c82b2cb24cc5b028aa76eb7b4ab24171ab3cdadb8356f", 6 | 7 | "32510ba9a7b2bba9b8005d43a304b5714cc0bb0c8a34884dd91304b8ad40b62b07df44ba6e9d8a2368e51d04e0e7b207b70b9b8261112bacb6c866a232dfe257527dc29398f5f3251a0d47e503c66e935de81230b59b7afb5f41afa8d661cb", 8 | 9 | "32510ba9aab2a8a4fd06414fb517b5605cc0aa0dc91a8908c2064ba8ad5ea06a029056f47a8ad3306ef5021eafe1ac01a81197847a5c68a1b78769a37bc8f4575432c198ccb4ef63590256e305cd3a9544ee4160ead45aef520489e7da7d835402bca670bda8eb775200b8dabbba246b130f040d8ec6447e2c767f3d30ed81ea2e4c1404e1315a1010e7229be6636aaa", 10 | 11 | "3f561ba9adb4b6ebec54424ba317b564418fac0dd35f8c08d31a1fe9e24fe56808c213f17c81d9607cee021dafe1e001b21ade877a5e68bea88d61b93ac5ee0d562e8e9582f5ef375f0a4ae20ed86e935de81230b59b73fb4302cd95d770c65b40aaa065f2a5e33a5a0bb5dcaba43722130f042f8ec85b7c2070", 12 | 13 | "32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd2061bbde24eb76a19d84aba34d8de287be84d07e7e9a30ee714979c7e1123a8bd9822a33ecaf512472e8e8f8db3f9635c1949e640c621854eba0d79eccf52ff111284b4cc61d11902aebc66f2b2e436434eacc0aba938220b084800c2ca4e693522643573b2c4ce35050b0cf774201f0fe52ac9f26d71b6cf61a711cc229f77ace7aa88a2f19983122b11be87a59c355d25f8e4", 14 | 15 | "32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd90f1fa6ea5ba47b01c909ba7696cf606ef40c04afe1ac0aa8148dd066592ded9f8774b529c7ea125d298e8883f5e9305f4b44f915cb2bd05af51373fd9b4af511039fa2d96f83414aaaf261bda2e97b170fb5cce2a53e675c154c0d9681596934777e2275b381ce2e40582afe67650b13e72287ff2270abcf73bb028932836fbdecfecee0a3b894473c1bbeb6b4913a536ce4f9b13f1efff71ea313c8661dd9a4ce", 16 | 17 | "315c4eeaa8b5f8bffd11155ea506b56041c6a00c8a08854dd21a4bbde54ce56801d943ba708b8a3574f40c00fff9e00fa1439fd0654327a3bfc860b92f89ee04132ecb9298f5fd2d5e4b45e40ecc3b9d59e9417df7c95bba410e9aa2ca24c5474da2f276baa3ac325918b2daada43d6712150441c2e04f6565517f317da9d3", 18 | 19 | "271946f9bbb2aeadec111841a81abc300ecaa01bd8069d5cc91005e9fe4aad6e04d513e96d99de2569bc5e50eeeca709b50a8a987f4264edb6896fb537d0a716132ddc938fb0f836480e06ed0fcd6e9759f40462f9cf57f4564186a2c1778f1543efa270bda5e933421cbe88a4a52222190f471e9bd15f652b653b7071aec59a2705081ffe72651d08f822c9ed6d76e48b63ab15d0208573a7eef027", 20 | 21 | "466d06ece998b7a2fb1d464fed2ced7641ddaa3cc31c9941cf110abbf409ed39598005b3399ccfafb61d0315fca0a314be138a9f32503bedac8067f03adbf3575c3b8edc9ba7f537530541ab0f9f3cd04ff50d66f1d559ba520e89a2cb2a83", 22 | 23 | "32510ba9babebbbefd001547a810e67149caee11d945cd7fc81a05e9f85aac650e9052ba6a8cd8257bf14d13e6f0a803b54fde9e77472dbff89d71b57bddef121336cb85ccb8f3315f4b52e301d16e9f52f904" 24 | ] 25 | 26 | cts = tuple(map(ord, c.decode('hex')) for c in ctshex); 27 | 28 | target = "32510ba9babebbbefd001547a810e67149caee11d945cd7fc81a05e9f85aac650e9052ba6a8cd8257bf14d13e6f0a803b54fde9e77472dbff89d71b57bddef121336cb85ccb8f3315f4b52e301d16e9f52f904" 29 | 30 | def strxor(a, b): # xor two strings of different lengths 31 | if len(a) > len(b): 32 | return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a[:len(b)], b)]) 33 | else: 34 | return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b[:len(a)])]) 35 | 36 | def strxornums(a, b): # xor two strings of different lengths 37 | if len(a) > len(b): 38 | return [x^y for (x, y) in zip(a[:len(b)], b)] 39 | else: 40 | return [x^y for (x, y) in zip(a, b[:len(a)])] 41 | 42 | def encrypt(key, msg): 43 | c = strxor(key, msg) 44 | return c 45 | 46 | -------------------------------------------------------------------------------- /ps1/solutions.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | 1. 3 | 4 | Compress then encrypt because compression adds entropy and encryption removes 5 | redundancy/reduces compressibility. 6 | 7 | -------------------------------------------------------------------------------- 8 | 2. 9 | 10 | G'(k)=G(k xor 1^s) is secure because xor with 1 just flips the bits and does 11 | not change the information content of the string. 12 | 13 | G'(k) = reverse(G(k)) is secure because a secure PRG does not yeild additional 14 | information when reversed. 15 | 16 | G'(k) = G(k)[0,...,n-2] is secure because if an attacker cannot guess the 17 | value of G(k)[n-2] given 0-(n-1), then it does not help him to be deprived of 18 | G(k)[n-1]. 19 | 20 | -------------------------------------------------------------------------------- 21 | 3. 22 | 23 | If A outputs LSB(x), then 24 | 25 | Adv[A, G'] = |Pr[A(G')=1] - Pr[A(r)=1]| 26 | = | Pr[LSB(G1 and G2) = 1] - 1/2 | 27 | = 1/4 28 | 29 | -------------------------------------------------------------------------------- 30 | 4. 31 | 32 | The bank has generated the following keys: 33 | k, k1, and k2 are random 34 | k1' = k xor k1 35 | k2' = k xor k2 36 | 37 | All choices involve p1 = (k1, k2) and p3 = (k2'), so we consider p2. 38 | 39 | p2 cannot be (k1,k2) because p2 U p1 would give p2 no additional information. 40 | 41 | p2 cannot be (k1',k2') because then p2 U p3 would give p2 no more info. 42 | 43 | p2 must be (k1',k2). p1 U p2 can derive k and p2 U p3 can derive k. 44 | 45 | p2 cannot be (k1') becaue p2 U p3 would be insufficient to derive k. 46 | 47 | p2 cannot be (k2,k2') because k2' xor k2 = k so p2 could derive k. 48 | 49 | -------------------------------------------------------------------------------- 50 | 5. 51 | 52 | We aim to show that for all c in C, m1 in M, m2 in M: 53 | 54 | P[E(k,m1) = c] = P[E(k,m2) = c] 55 | 56 | so we simply compute, for any m in M 57 | P[E(k,m) = c] = P[m + k mod 256 == c] = P[c - m mod 256 == k] = 1/256 58 | 59 | So the cipher has perfect secrecy. 60 | 61 | -------------------------------------------------------------------------------- 62 | 6. 63 | 64 | E'(k,m) = E(k,m) || E(k,m) appears to be ss. 65 | 66 | E'((k,k'), m) = E(k,m) || E(k',m) appears to be ss. 67 | 68 | E'(k,m) = E(k,m) || LSB(m) is not ss because the adversary can send two 69 | messages with different LSBs to determine which ciphertext is which. 70 | 71 | E'(k,m) = E(0^n,m) is not ss since the adversary can send any two distinct 72 | messages and determine which is which. 73 | 74 | E'(k,m) = 0 || E(k,m) appears to be ss. 75 | 76 | E'(k,m) = E(k,m) || k is obviously not ss because the 77 | attacker can send m0=0s, m1=1s and just look for the message that is k||k. 78 | 79 | -------------------------------------------------------------------------------- 80 | 7. 81 | 82 | From python code, 83 | k = 0d07a14569fface7ec3ba6f5f623 84 | "attack at dusk" -> 6c73d5240a948c86981bc2808548 85 | 86 | -------------------------------------------------------------------------------- 87 | 8. 88 | 89 | We need to encrypt the key with 1, 6, 11, and 26 so that (25) does not have 90 | a way to decrypt the key, but everybody else does. 91 | 92 | -------------------------------------------------------------------------------- 93 | 9. 94 | 95 | We need log(n) keys because we have to encrypt the sibling, parent's 96 | sibling, parent's parent's sibling, and so on up the log(n) levels. 97 | 98 | -------------------------------------------------------------------------------- 99 | 10. 100 | 101 | We must encrypt k under the leaf siblings' keys 15, 17, and 26 as well as the 102 | nodes 11, 6, and 4. 103 | 104 | -------------------------------------------------------------------------------- 105 | -------------------------------------------------------------------------------- /pa4/po_attack.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import urllib 4 | import urllib.error 5 | import urllib.request 6 | import multiprocessing.dummy 7 | import sys 8 | 9 | import operator 10 | 11 | from itertools import islice 12 | from itertools import repeat 13 | from itertools import chain 14 | 15 | from binascii import hexlify 16 | from binascii import unhexlify 17 | 18 | def grouper(iterable, n, fillvalue=None): 19 | "Collect data into fixed-length chunks or blocks" 20 | # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx" 21 | args = [iter(iterable)] * n 22 | return zip_longest(*args, fillvalue=fillvalue) 23 | 24 | #-------------------------------------------------------------- 25 | # padding oracle 26 | #-------------------------------------------------------------- 27 | class PaddingOracle(object): 28 | block_size = 16 29 | hex_block_size = 2*block_size 30 | 31 | def __init__(self, targetURL, ct): 32 | self._targetURL = targetURL 33 | 34 | self._ct = ct 35 | 36 | self._numPTBlocks = int(len(self._ct)/self.block_size) - 1 37 | self._ptGuesses = [bytearray(self.block_size) for i in range(self._numPTBlocks)] 38 | 39 | 40 | def attack(self): 41 | #poolSZ = 64 42 | #pool = multiprocessing.Pool(poolSZ) 43 | 44 | for block in range(0, self._numPTBlocks): 45 | self._attack_block(block) 46 | 47 | return b''.join(self._ptGuesses) 48 | 49 | def _attack_block(self, block): 50 | poolSZ = 128 51 | pool = multiprocessing.dummy.Pool(poolSZ) 52 | 53 | for blockPos in reversed(range(self.block_size)): 54 | print("Guessing [{}][{}]".format(block, blockPos)) 55 | 56 | res = pool.map(self.query, 57 | ((islice(self._ct, block*self.block_size), 58 | self._guess_block(block, blockPos, g), 59 | islice(self._ct, (block+1)*self.block_size, (block+2)*self.block_size)) 60 | for g in range(128))) 61 | 62 | res = list(res) 63 | 64 | try: 65 | value = next(v for v,correct in enumerate(res) if correct) 66 | except StopIteration: 67 | print("Stopped") 68 | # This is the start of the pad at the end of the message 69 | value = next(v for v,correct in enumerate(res) if correct is None) 70 | 71 | print("Correctly guessed [{}][{}] = {}".format(block, blockPos, value)) 72 | self._ptGuesses[block][blockPos] = value 73 | 74 | def _guess_block(self, block, blockPos, value): 75 | padLen = self.block_size - blockPos 76 | ctPos = block*self.block_size 77 | 78 | guessBlock = self._ptGuesses[block][:] 79 | guessBlock[blockPos:] = map(operator.xor, islice(guessBlock, blockPos, None), repeat(padLen)) 80 | guessBlock[blockPos] = guessBlock[blockPos] ^ value 81 | guessBlock[:] = map(operator.xor, islice(guessBlock, None), islice(self._ct, ctPos, ctPos + self.block_size)) 82 | 83 | return guessBlock 84 | 85 | 86 | def query(self, parts): 87 | queryHex = hexlify(bytes(chain.from_iterable(parts))) 88 | target = self._targetURL + queryHex.decode("ascii") 89 | 90 | req = urllib.request.Request(target) 91 | 92 | try: 93 | status = urllib.request.urlopen(req) 94 | except urllib.error.URLError as e: 95 | status = e.code 96 | assert(status in (403, 404)) 97 | return status == 404 98 | 99 | 100 | def self_test(): 101 | targetURL = 'http://crypto-class.appspot.com/po?er=' 102 | 103 | target = b'f20bdba6ff29eed7b046d1df9fb7000058b1ffb4210a580f748b4ac714c001bd4a61044426fb515dad3f21f18aa577c0bdf302936266926ff37dbf7035d5eeb4' 104 | 105 | po = PaddingOracle(targetURL, unhexlify(target)) 106 | pt = po.attack() 107 | 108 | print("Plain Text") 109 | print(pt.decode("ascii")) 110 | 111 | print("Plain Text Hex") 112 | print(hexlify(pt)) 113 | 114 | 115 | if __name__ == "__main__": 116 | self_test() 117 | -------------------------------------------------------------------------------- /pa1/decrypt.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python2.7 2 | 3 | from __future__ import print_function 4 | from ct import * 5 | 6 | import operator 7 | import sys 8 | 9 | from functools import reduce 10 | from functools import partial 11 | 12 | def compose(f, g): 13 | def composition(*args, **kwargs): 14 | return f(g(*args, **kwargs)) 15 | return composition 16 | 17 | guesses = { 18 | 0: { 19 | 34:'u', 20 | 44:'p',46:'t', 21 | 68:'o',69:'r',70:' ',71:'t', 22 | 78:'b',79:'e' 23 | }, 24 | 25 | 1: { 26 | 13:'r',14:'o' 27 | }, 28 | 2: { 29 | 17:'o',19:'t', 30 | 9:'t',10:'h' 31 | }, 32 | 3: { 33 | 20:'c',21:'e', 34 | 35:'n',36:'c',38:'y',39:'p',40:'t',41:'i',42:'o',43:'n', 35 | 49:'r',50:'i',51:'t',53:'m', 36 | 66:'o', 37 | 73:'i',74:'p',81:'t' 38 | }, 39 | 5: { 40 | 30:'r',31:'a',32:'p',33:'h' 41 | }, 42 | 6: { 43 | 82:'r',83:'c',84:'e' 44 | }, 45 | 8: { 46 | 60:'l' 47 | }, 48 | 9: { 49 | 2:'h',3:'e', 50 | 5:'C',6:'o',7:'n', 51 | 22:'t',23:'i',25:'n', 52 | 26:'a',27:'r',28:'y', 53 | 54:'s',55:' ',56:'t',57:'h',58:'e', 54 | 63:' ',64:'o' 55 | }, 56 | 57 | # Don't trust these... 58 | 10: {} 59 | } 60 | 61 | def guessed_key(guesses): 62 | key = {} 63 | for msgNum, letters in guesses.viewitems(): 64 | for pos, letter in letters.viewitems(): 65 | keyVal = ord(letter) ^ cts[msgNum][pos] 66 | value = key.get(pos) 67 | if value is not None: 68 | if value != keyVal: 69 | print("WRONG GUESS at message {}, letter {}".format(msgNum, pos)) 70 | print("---> {} != {}.".format(chr(keyVal), chr(value))) 71 | else: 72 | key[pos] = keyVal 73 | 74 | return key 75 | 76 | def xor_is_space(ch): 77 | c = ch[1] 78 | return (ord('a') <= c and c <= ord('z')) or (ord('A') <= c and c <= ord('Z')) 79 | 80 | get_0th_for_each = partial(map, operator.itemgetter(0)) 81 | filter_spaces = partial(filter, xor_is_space) 82 | 83 | space_positions = compose(set, compose(compose(get_0th_for_each, filter_spaces), enumerate)) 84 | 85 | def make_vertical(msg): 86 | return "\n".join("{} {}".format(l,i) for i,l in enumerate(msg)) 87 | 88 | def print_msgs(msgs, vertical): 89 | if vertical: 90 | msgs = map(make_vertical, msgs) 91 | 92 | for i, msg in enumerate(msgs): 93 | print("Messages[{}] = \n{}\n".format(i,msg)) 94 | 95 | def decrypt_messages(msgs, key): 96 | targetPTs = [["*" for i in xrange(len(ct))] for ct in msgs] 97 | 98 | for i, targetStr in enumerate(msgs): 99 | for idx,v in key.viewitems(): 100 | if idx < len(targetPTs[i]): 101 | targetPTs[i][idx] = chr(targetStr[idx] ^ v) 102 | 103 | return ["".join(targetPT) for targetPT in targetPTs] 104 | 105 | def do_decrypt(msgNum): 106 | crossXors = [[strxornums(a,b) for b in cts if b is not a] for a in cts] 107 | spaceSets = map(partial(map, space_positions), crossXors) 108 | spaceIndices = map(partial(reduce, set.intersection), spaceSets) 109 | spaceIndices = [reduce(set.intersection, s) for s in spaceSets] 110 | 111 | key = guessed_key(guesses) 112 | for (ct, indices) in zip(cts, spaceIndices): 113 | for i in indices: 114 | keyVal = ord(" ") ^ ct[i] 115 | value = key.get(i) 116 | if value is not None: 117 | if value != keyVal: 118 | print("Mismatch! {} != {}.".format(keyVal, value)) 119 | else: 120 | key[i] = keyVal 121 | 122 | cts_short = cts #[ct[:len(cts[-1])] for ct in cts] 123 | 124 | if msgNum is None: 125 | toDecrypt = cts_short 126 | else: 127 | toDecrypt = (cts_short[int(msgNum)],) 128 | 129 | 130 | messages = decrypt_messages(toDecrypt, key) 131 | 132 | # Print single messages vertically 133 | print_msgs(messages, len(messages) == 1) 134 | 135 | 136 | if __name__ == "__main__": 137 | do_decrypt(sys.argv[1] if len(sys.argv) > 1 else None) 138 | -------------------------------------------------------------------------------- /ps2/solutions.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | 1. 3 | 4 | The events have the following probabilities: 5 | 1. 2^-128 ~= 10^-38.5 6 | 2. 10^-6 7 | 3. 10^-30 8 | 4. 10^-36 9 | 5. 10^-42 10 | 11 | So the probability order from most to least likely is 12 | 2,3,4,1,5 13 | 14 | -------------------------------------------------------------------------------- 15 | 2. 16 | 17 | At $200 each With 4 trillion = $4*10^12, the orgainization could 18 | purchase 2*10^10 such machines. With each machine testing 10^9 keys per second, 19 | the orgainization could test 2*10^19 ~= 2^64.11 keys/second. 20 | 21 | There 2^128 different AES keys. To test all possible keys, it would take 22 | roughly 2^64 seconds, which is roughly 5.8*10^11 years. 23 | That's more than 10^9 years. 24 | 25 | -------------------------------------------------------------------------------- 26 | 3. 27 | 28 | a. 29 | { F(k,x) when x != 0^n 30 | { 0^n otherwise 31 | 32 | This function is _not secure_ because the adversary would submit 0^n and guess 33 | that that the function is not random if the output is zero. 34 | So Adv[A,F] = |0 - 1| = 1 35 | 36 | b. 37 | { F(k,x) when x != 0^n 38 | { k otherwise 39 | 40 | Not sure about this one. I do not think it's secure 41 | 42 | c. 43 | { F(k1, x) when x != 0^n 44 | { k2 otherwise 45 | 46 | Despite the fact that a key is being sent, this function _is secure_ because 47 | when the adversary submits nonzero messages, he learns only as much about F' 48 | as he would learn about F, but F is secure. When he submits 0^n, he gets a 49 | value that is chosen uniformly from K, so it is indistinguishable from random. 50 | 51 | d. F(k, x ^ 1^n) 52 | 53 | This function _is secure_ it is equivalent inverting the input of F, which 54 | cannot reveal any information to the adversary since F is secure. 55 | 56 | e. F(k1, x) || F(k2, x) 57 | 58 | This function _is secure_. If there were an adversary A who had an advantage for 59 | this function, we could create an adversary B with an advantage for F as 60 | follows. 61 | 62 | When A sends a message m_i, B sends m_i. B takes the response R and 63 | sends R || R to A. A has a non-negligible probability of determining whether R 64 | is random or F(k, m_i). 65 | 66 | 67 | f. k ^ x 68 | 69 | This function is _not secure_ because the adversary can send m1=0, then send 70 | m2 = f(m1) xor X. If b=0, he will always get back X and will therefore guess 71 | b=1 if he does not. If b=1, he will not get back X and will guess correctly 72 | again. So Adv[A,F] = 1. 73 | 74 | -------------------------------------------------------------------------------- 75 | 4. 76 | 77 | When the input is 0^64: 78 | R0 = 0, L0=0 79 | R1 = F(k1,0), L1=0 80 | R2 = F(k2, F(k1,0)), L2 = F(k1,0) 81 | 82 | When the input is 1^32 || 0^32: 83 | R0 = 0, L0 = 1 84 | R1 = 1 xor F(k1, 0), L1 = 0 85 | R2 = F(k2, 1 xor F(k1, 0)), L2 = 1 xor F(k1, 0) 86 | 87 | so the first L2 should be the inverse of the second L2. 88 | This is the case for the pair _e86..., 179_ 89 | 90 | -------------------------------------------------------------------------------- 91 | 5. 92 | 93 | First, note that 94 | c0 = F(k, F(k, 0)) 95 | c1 = F(k, F(k, F(k, 0))) 96 | and that c1 = F(k, c0) 97 | 98 | In response to the attacker's request for the encryption of m1=c0 ^ c1 with 99 | nonce c0, the attacker receives: 100 | 101 | nonce | c'0 102 | c0 | F(k,F(k,c0)^m1) = F(k,F(k,c0)^c0^c1) = F(k,c1^c0^c1) = F(k,c0) = c1 103 | 104 | So _c'0 = c1_ 105 | 106 | -------------------------------------------------------------------------------- 107 | 6. 108 | 109 | If block number l/2 is corrupted, then that block and each block that comes 110 | after will be corrupted, so _2_ blocks of plaintext will be corrupted. 111 | 112 | -------------------------------------------------------------------------------- 113 | 7. 114 | 115 | With ctr, the counter value used to decrypt each block of ciphertext does not 116 | depend on the value of any other block. Therefore with one block corrupted, 117 | only _1_ block of plaintext will be corrupted. 118 | 119 | -------------------------------------------------------------------------------- 120 | 8. 121 | 122 | The strings have length: 123 | 124 | 'If qual...' -> 124 125 | 'The sig...' -> 165 126 | 'In this...' -> 108 127 | 'To cons...' -> 221 128 | 129 | Each character is encoded to one byte. The messages of length 165 and 221 130 | are too big to encrypt to 128 bytes. The message of length 124 could not have 131 | been encrypted to 128 bytes because the IV requires 16 bytes to send. 132 | 133 | So the message of length 108 encrypted to 124 bytes, with 4 bytes of padding. 134 | 135 | -------------------------------------------------------------------------------- 136 | 9. 137 | 138 | We are given to following. 139 | k[0]^k[2]^k[3] = 0011 140 | k[0]^k[2]^k[4] = 1010 141 | k[0]^k[1]^k[2]^k[3] = 0110 142 | 143 | 144 | We aim to find k[0]^k[1]^k[2]^k[4]. 145 | 146 | k[0]^k[1]^k[2]^k[4] = 0110^k[3]^k[4] = 0110^1010^k[0]^k[2]^k[3] 147 | = 0110^1010^0011 = _1111_ 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- 152 | -------------------------------------------------------------------------------- /final/solutions.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | 1. 3 | 4 | Error correcting codes are designed to be tolerant against random changes 5 | during transmission. If the ECC encoding is permuted randomly by an 6 | encryption function, it won't work correctly. 7 | 8 | Even more importantly, decryption needs an error free input to work 9 | effectively. If a system tries to decrypt an erroneous ciphertext, the 10 | resulting pre-ECC plaintext will be meaningless. 11 | 12 | Therefore you should encrypt and then apply the ECC. 13 | 14 | -------------------------------------------------------------------------------- 15 | 2. 16 | 17 | For any bit X[i], we have 18 | 19 | P(X[i] == 0) = p = 1/2 20 | 21 | so 22 | 23 | P(X[i] xor Y[i] == 0) 24 | = P(X[i] == 0 and Y[i] == 0 or X[i] == 1 and Y[i] == 1) 25 | = P(X[i] == 0 and Y[i] == 0) + P(X[i] == 1 and Y[i] == 1) 26 | = p*P(Y[i]==0) + (1-p)*P(Y[i]==1) 27 | 28 | Letting q(i, ...) = q = P(Y[i]==0) 29 | = pq + (1-p)*(1-q) 30 | = pq + 1 - q - p + qp 31 | = 1 + q - q - 1/2 32 | = 1/2 33 | 34 | 35 | Since i was arbitrary, the result Z= X ^ Y is a uniform distribution over 36 | {0,1}^n 37 | 38 | So 0^n occurs with probability 1/2^n 39 | 40 | -------------------------------------------------------------------------------- 41 | 3. 42 | 43 | Since (E1,D1) is deterministic, it cannot offer many time semantic security. 44 | However, it can offer one time semantic security. Since the key space is 45 | smaller than the message space, the cipher cannot be perfectly secure. 46 | 47 | On the other hand, (E2,D2) can be both one time semantically secure and 48 | perfectly secure since the key space and message space are the same size. 49 | 50 | -------------------------------------------------------------------------------- 51 | 4. 52 | 53 | CBC mode requires the block cipher be reversible, so it must be a PRP. On 54 | the other hand, CTR mode can be configured to only use the encryption directly 55 | of the block cipher, so it will work with just a PRF. 56 | 57 | -------------------------------------------------------------------------------- 58 | 5. 59 | 60 | The function G(k)[m] with m in {0,1} is a secure PRF whenever G is a secure 61 | PRG. The other options are insecure because they are clearly distinguishable 62 | from random (change a single bit in the m and see what happens to the 63 | output.) 64 | 65 | 66 | -------------------------------------------------------------------------------- 67 | 6. 68 | 69 | If the same key is used to encrypt 2^32 messages, we need to have at least 70 | (2^32)^2 or 2^64 distinct, uniformly sampled nonces in order to have a <1/2 71 | probability of not having a nonce collision. So the nonce space should be 72 | 2^128. 73 | 74 | -------------------------------------------------------------------------------- 75 | 7. 76 | 77 | Since the nonces will not collide until the entire nonce space has been used, 78 | it is acceptable to use a nonce space the same size as the number of messages 79 | that are expected to be sent, or just 2^32 nonces. 80 | 81 | -------------------------------------------------------------------------------- 82 | 8. 83 | 84 | That a MAC is secure implies that an attacker cannot create any forgery. This 85 | implies the weaker version of security that says an attacker cannot forge the 86 | message S(k,m1) given m0, m1, and S(k,m0). 87 | 88 | -------------------------------------------------------------------------------- 89 | 9. 90 | 91 | Collision Resistance implies that it is difficult to find any 92 | 93 | m0, m1 such that H(m0) = H(m1) 94 | 95 | -------------------------------------------------------------------------------- 96 | 10. 97 | 98 | Authenticated Encryption implies many weaker security definitions. For 99 | instance, it implies: 100 | 101 | > Given m and E(k,m) it is difficult to find k (weak semantic security) 102 | 103 | > (E,D) provides chosen-ciphertext security. 104 | 105 | AE does not imply 106 | > Given c=E(k,m) for some k,m, the attacker cannot find k',m' such that 107 | > c=E(k',m'). That would imply some notion of "collision resistance" between 108 | > messages encrypted with different keys. 109 | 110 | > Given k,m and E(k,m) the attacker cannot create a valid encryption of m+1 111 | under key k. (AE does not imply anything if the attacker gets k) 112 | 113 | -------------------------------------------------------------------------------- 114 | 11. 115 | 116 | The following are true of DH key exchange: 117 | > The basic protocol enables key exchange secure against eavesdropping, but is 118 | > insecure against active adversaries that can inject and modify messages. 119 | 120 | > The protocol provides security against eavesdropping in any finite group in 121 | > which the Hash Diffie-Hellman (HDH) assumption holds. 122 | 123 | The following are not true of DH: 124 | > The protocol is based on the concept of a trapdoor function. (Even though 125 | > the protocol relies on a one-way function, there is no trapdoor to the 126 | > discrete log problem.) 127 | 128 | > As with RSA, the protocol only provides eavesdropping security in the group 129 | > Z*N where N is an RSA modulus. (RSA provides eavesdropping security in other 130 | > groups such as the elliptic curve groups, as does DH.) 131 | 132 | -------------------------------------------------------------------------------- 133 | 12. 134 | 135 | Each party Ai has access to the values ai and of course Xi = g^ai and Yi = 136 | Xi^b = g^bai. Ai can therefore compute g^b as 137 | (g^bai) ^ 1/ai = Xi^b ^ 1/ai = Yi^(1/ai) 138 | 139 | -------------------------------------------------------------------------------- 140 | 13. 141 | 142 | For any prime p, totient(p) = p-1, so d is the inverse of e in the group 143 | Z*_{p-1}, or 144 | 145 | d <- e^-1 (mod p-1) 146 | 147 | -------------------------------------------------------------------------------- 148 | -------------------------------------------------------------------------------- /ps6/solutions.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | 1. 3 | 4 | Ciphertexts that are the same length as their corresponding plaintext are not 5 | MACed, so they cannot gaurentee authenticated encryption. Therefore public-key 6 | systems with "short" ciphertexts can never be secure. 7 | 8 | -------------------------------------------------------------------------------- 9 | 2. 10 | 11 | Suppose (G, E, D) is a deterministic public key encryption system. An attacker 12 | can win the CPA game by submitting any message pair (m0, m1) with m0 != m1 to 13 | receive challenge ciphertext c. He then encrypts both m0 and m1 to get c0,c1. 14 | He then outputs b' = 1 if c == 1 else 0. He wins the game with advantage 1. 15 | He can use the same strategy to win the CCA game. 16 | 17 | Therefore no deterministic public key encryption system can be semantically 18 | secure. 19 | 20 | -------------------------------------------------------------------------------- 21 | 3. 22 | 23 | Let (Gen, E, D) be CCA secure pk encryption system with M = {0,1}^128. 24 | 25 | >E' = (E(pk,m), 0^128) and D'(sk, (c1,c2)) = D(sk,c1) 26 | Phase 1: 27 | Attacker sends nothing. 28 | Challenge: 29 | Attacker picks any distinct m0, m1 with E(pk,m0) = c0 and E(pk,m1)=c1. 30 | Attacker sends (m0,m1) to receive (cb, 0^128) 31 | Phase 2: 32 | Attacker sends (cb, 1^128) to receive D(sk, cb) from which he determines b 33 | with advantage 1. 34 | So the system is not CCA secure. 35 | 36 | >E' = (E(pk,m), E(pk,m)) and D'(sk, (c1,c2)) = c1 == c2 ? D(k2, c1) : bottom 37 | Suppose there is a CCA on this system. We can then attack (G,E,D) with the 38 | following transformations: 39 | Phase 1: 40 | ci -> (ci,ci) 41 | mi -> (mi,mi) 42 | Challenge: 43 | (m0,m1) -> ((m0,m0),(m1,m1)) 44 | E(pk,mb) -> (c,c) := (E(pk,mb), E(pk,mb)) 45 | Phase 2: 46 | ci -> (ci,ci) 47 | mi -> (mi,mi) 48 | Since ci != c, (ci,ci) != (c,c) 49 | We then output b' directly to win the CCA game. 50 | So the system is CCA secure. 51 | 52 | >E' = E(pk, m + 1^128) and D' = D(sk,c) + 1^128 53 | Suppose there is a CCA on this system. We can then attack (G,E,D) with the 54 | following transformations: 55 | Phases 1 and 2: 56 | ci -> ci 57 | mi -> ~mi 58 | Challenge: 59 | (m0,m1) -> (~m0,~m1) 60 | E(pk,~mb) -> c (unchanged) 61 | We then output b' directly to win the CCA game. 62 | So the system is CCA secure. 63 | 64 | >E' = (E(pk,m),E(pk,m)) and D'(sk, (c1,c2))=D(k2,c1) 65 | Phase 1: 66 | Attacker sends nothing. 67 | Challenge: 68 | Attacker picks any distinct m0, m1 with E(pk,m0) = c0 and E(pk,m1) = c1. 69 | Attacker sends (m0,m1) to receive (cb, cb) 70 | Phase 2: 71 | Attacker sends (cb, ~cb) to receive D(sk, cb) == mb from which he determines 72 | b with advantage 1. 73 | So the system is not CCA secure. 74 | 75 | -------------------------------------------------------------------------------- 76 | 4. 77 | 78 | Alice aims to find an integer multiple of phi(N). She knows that 79 | 3*da mod phi(N) = 1 80 | 3*da = k*phi(N) + 1 81 | 3*da - 1 = k*phi(N) 82 | 83 | So (3*da - 1) is an integer multiple of phi(N). 84 | 85 | -------------------------------------------------------------------------------- 86 | 5. 87 | 88 | Alice has found a value y with the property that 89 | 90 | { y = 1 mod p = kp + 1 91 | { y = -1 mod q = lq - 1 92 | 93 | or 94 | 95 | { y = -1 mod p = kp - 1 96 | { y = 1 mod q = lq + 1 97 | 98 | Therefoer gcd(N, y-1) is either p or q. In either case, she can easily find the 99 | factors of N by letting p = gcd(N, y-1) and q = N/p. 100 | 101 | -------------------------------------------------------------------------------- 102 | 6. 103 | 104 | The totient function is multiplicative, so 105 | phi(N) 106 | = phi(pqr) 107 | = phi(p)phi(q)phi(r) 108 | = (p-1)(q-1)(r-1) 109 | 110 | -------------------------------------------------------------------------------- 111 | 7. 112 | 113 | We can compute s1^a * s2^b = s^(ar1) * s^(br2) = s^(ar1 + br2) = s^1 = s 114 | 115 | -------------------------------------------------------------------------------- 116 | 8. 117 | 118 | The attacker asks for the encryption of m0 and m1, which are 119 | E(pk,m0) = (c0,c1) = (g^r, m0*g^xr) for random r 120 | 121 | and 122 | 123 | E(pk,m1) = (c2,c3) = (g^r, m1*g^xr) for random r 124 | 125 | He wishes to compute 126 | E(pk,m0*m1) = (g^r, m0*m1*g^xr) for random r 127 | 128 | He can compute this value by picking a random r in Zn, then computing 129 | (c0*c2, c1*c3) = (g^(r1+r2), m0*m1*h^(r1+r2)) 130 | 131 | 132 | -------------------------------------------------------------------------------- 133 | 9. 134 | 135 | If a1 + a2 = a, then we can can share the computation of v by recognizing that 136 | v = u^a = u^(a1 + a2) = u^a1 * u^a2. 137 | 138 | So party 1 computes u1 = u^a1, party 2 computes u2 = u^a2, and the results are 139 | combined using v = u1 * u2. 140 | 141 | -------------------------------------------------------------------------------- 142 | 10. 143 | 144 | Since B2 = (A2/g^b)^r * A0^s and A2 = g^(xy + a), 145 | 146 | B1 = (A1^r * g^s) = g^(yr+s) 147 | B2 = (A2/g^b)^r * A0^s = g^(r(xy+a-b) + sx) 148 | 149 | So considering the exponents 150 | B1^x = rxy + sx 151 | B2 = rxy + (ra - rb) + sx 152 | 153 | If a == b then 154 | B2/B1^x = rxy + (0) + sx - rxy - sx = 0 in the exponent 155 | 156 | So she checks that B2/B1^x == 1 157 | 158 | -------------------------------------------------------------------------------- 159 | 11. 160 | 161 | In the proof of the bounds on Wiener's attack where N=pq, we found that 162 | |N - phi(N)| <= p+q <= 3 sqrt(N) 163 | 164 | When N is product of 3 primes p,q,r we see that 165 | phi(N) = pqr - pq - pr - qr + p + q + r - 1 166 | |N - phi(N)| <= 4*N^(2/3) 167 | 168 | Nothing else in the proof changes, so we require d <= N^(1/6)/c 169 | 170 | -------------------------------------------------------------------------------- 171 | -------------------------------------------------------------------------------- /ps3/solutions.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | 1. 3 | 4 | Since only the file's contents and not its name, size, or other metadata are 5 | being MACed, the file can be tampered with by _changing the file's last 6 | modification time_. 7 | 8 | -------------------------------------------------------------------------------- 9 | 2. 10 | 11 | --S(k, m xor 1^n) and V(k, m xor 1^n,t) 12 | Suppose an attacker can forge a pair (m', t') under (S',V') by sending some 13 | sequence of messages m_i and receiving tags t_i. He could then send m_i xor 14 | 1^n to a (S, V) challenger and receive the same tags t_i, then forge the pair 15 | (m' xor 1^n, t'). This implies that (S, V) is not secure. Therefore the MAC 16 | _is secure_. 17 | 18 | --S(k, m[0,...,n-2] || 0) and V(k, m[0,...,n-2] || 0, t) 19 | This MAC _is not secure_ because can attacker can ask for a message pair (m,t) 20 | with m[n-1] != 0 and produce the forgery (m[0,...,n-1] || 0, t) 21 | 22 | --[t <- S(k,m), output (t1,t2)] and [t1 == t2 ? V(k,m,t1) : "0"] 23 | If an attacker could generate a forgery (m', (t1', t2')) on (S',V') then that 24 | implies that V'(k, m, (t1', t2')) = 1, so t1'==t2' and V(k,m',t1') = 1. Hence 25 | the attacker can forge the pair (m',t1') on implying (S, V) is not secure. 26 | Therefore the MAC _is secure_. 27 | 28 | --S(k,m) and [m != 0^n ? V(k,m,t) : "1"] 29 | This MAC _is not secure_ because an attacker can trivially create a forgery, 30 | namely (0^n, t), where t is any sequence of 128 bits. 31 | 32 | --[m == 0^n ? S(k, 1^n) : S(k,m)] and [m == 0^n ? V(k,1^n,t) : V(k,m,t)] 33 | The MAC _is not secure_. The attacker can send m_1 = 0^n to receive tag 34 | t_1=S(k,1^n). The attacker can then forge the pair (m,t) = (1^n, t_1) 35 | 36 | --S(k,m)[0,...126] and [V(k,m,t||0) or V(k,m,t||1)] 37 | If an attacker can forge a message pair on (S',V') with probability P, then we 38 | can forge a message pair on (S,V) with probability P/2. Since P/2 is 39 | negligible, P is also negligible. So the MAC _is secure_. 40 | 41 | -------------------------------------------------------------------------------- 42 | 3. 43 | 44 | The attacker found that m -> (r,t). In other words, t=F(k1, F(k, (m xor r))). 45 | The attacker can forge _(m xor 1^n, (r xor 1^n, t))_ because ECBC checks that 46 | t = F(k1, F(k, (m' xor r'))) = F(k1, F(k, (m xor 1^n xor r xor 1^n))). 47 | 48 | We see from the above that this is true and ECBC outputs "1" 49 | 50 | -------------------------------------------------------------------------------- 51 | 4. 52 | 53 | For this scheme to work, no user can have a superset of the keys of any other 54 | user. We can see from the options that 55 | 56 | S1 = {k1} ... S4 = {k1,k3} 57 | 58 | ... S4 = {k2,k3,k4} ... S6 = {k3,k4} 59 | 60 | S1 = {k1,k2} ... S5 = {k1,k2} 61 | 62 | are all invalid. This leaves the remaining option as the only valid choice. 63 | 64 | S1={k2,k4}, S2={k2,k3}, S3={k3,k4}, S4={k1,k3}, S5={k1,k2}, S6={k1,k4} 65 | 66 | -------------------------------------------------------------------------------- 67 | 5. 68 | 69 | We would call AES once to undo the final encryption stage, once to undo the 70 | last CBC stage, then we would xor the final bin of the CBC state, recompute 71 | F(k, m'), then compute F(k1, .). So we would have to call AES _4 times_. 72 | 73 | -------------------------------------------------------------------------------- 74 | 6. 75 | 76 | H′(m)=H(m)[0,…,31] (i.e. output the first 32 bits of the hash) 77 | 78 | This hash function _is not collision resistant_ because the tag space is too 79 | small. An attacker can simply hash 2^16 messages to have a 1/2 chance of 80 | finding a collision. 81 | 82 | H′(m)=H(H(m)) 83 | 84 | Suppose an attacker finds m1 and m2 so that H(H(m1)) == H(H(m2)). Then the 85 | attacker could simply let H(m1) = m1' and H(m2) = m2' and show that H(m1') = 86 | H(m2'). Since H is collision resistant, no such (m1', m2') can be found so 87 | the hash _is collision resistant_. 88 | 89 | H′(m)=H(H(H(m))) 90 | 91 | The hash _is collision resistant_ from the same argument as for H(H(m)). 92 | 93 | H′(m)=H(|m|) (i.e. hash the length of m) 94 | 95 | The hash clearly _is not collision resistant_ because an attacker could 96 | produce a collision with any (m1,m2) where |m1| == |m2| 97 | 98 | H′(m)=H(0) 99 | 100 | The hash clearly _is not collision resistant_ because all messages collide. 101 | 102 | H′(m)=H(m || 0) 103 | 104 | The hash _is collision resistant_. If an attacker can find m1, m2 such that 105 | H'(m1) == H'(m2), then he could produce H(m1 || 0) == H(m2 || 0). Since H is 106 | collision resistant, such a pair (m1,m2) does not exist. 107 | 108 | H′(m)=H(m) xor H(m xor 1^|m|) (where m xor 1^|m| is the complement of m) 109 | 110 | This hash _is not collision resistant_. An attacker can produce any pair 111 | (m, ~m) -> (H(m) xor H(~m), H(~m) xor H(m)) which is a collision. 112 | 113 | -------------------------------------------------------------------------------- 114 | 7. 115 | 116 | 117 | Either H1(x) == H1(y), or it does not. If it does, then x and y are a 118 | collision for H1. If it does not, then H1(x) and H1(y) are a collision for 119 | H2. 120 | 121 | A little too easy....? 122 | 123 | -------------------------------------------------------------------------------- 124 | 8. 125 | 126 | f1(x,y) = AES(y,x) xor y 127 | 128 | We aim to find x1,y1,x2,y2 such that 129 | 130 | AES(y1,x1) xor y1 = AES(y2,x2) xor y2 131 | -> AES(y1,x1) = AES(y2,x2) xor y2 xor y1 132 | 133 | So first just let x2,y2 = (0^n, 1 || 0^n-1) and y1 = 11 || 0^n-2. 134 | Then AES(y2,x2) = f5569b3ab6a6d11efde1bf0a64c6854a 135 | so AES(y1,x1) = f5569b3ab6a6d11efde1bf0a64c6854a xor 0^n xor 11 || 0^n-2 136 | = e4569b3ab6a6d11efde1bf0a64c6854a 137 | so x1 = bc042352a96d8509fd1722c082c85c0c 138 | 139 | -------------------------------------------------------------------------------- 140 | 9. 141 | 142 | f2(x,y) = AES(x,x) xor y 143 | 144 | The result can be computed similar to the method in problem 8. 145 | 146 | -------------------------------------------------------------------------------- 147 | 10. 148 | 149 | The bound must be greater than for a 2 way collision, so O(|T|^2/3) makes 150 | sense... 151 | 152 | -------------------------------------------------------------------------------- 153 | -------------------------------------------------------------------------------- /pa2/bc_modes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import Crypto.Cipher.AES as AES 4 | 5 | from itertools import islice 6 | from itertools import chain 7 | from itertools import repeat 8 | from itertools import starmap 9 | 10 | from functools import partial 11 | 12 | import operator 13 | 14 | def compose(f, g): 15 | def composition(*args, **kwargs): 16 | return f(g(*args, **kwargs)) 17 | return composition 18 | 19 | def xor_block(left, right): 20 | return map(operator.xor, left, right) 21 | 22 | def to_bytes(cnt, val): 23 | return bytes((val & (0xFF << pos*8)) >> pos*8 for pos in range(cnt)) 24 | 25 | def from_bytes(val): 26 | if val is None: 27 | return None 28 | 29 | try: 30 | return int(val) 31 | except: 32 | pass 33 | 34 | result = 0 35 | m = 1 36 | for b in val: 37 | result += m*b 38 | m *= 256 39 | 40 | return result 41 | 42 | class CBCCipher(object): 43 | def __init__(self, blockCipher, key): 44 | self._cipher = blockCipher.new(key) 45 | 46 | def get_block_size(self): 47 | return self._cipher.block_size 48 | 49 | def encrypt(self, plainText, IV): 50 | blockSZ = self.get_block_size() 51 | 52 | IV = bytes(IV) 53 | if len(IV) != blockSZ: 54 | raise ValueError("Bad IV") 55 | 56 | if not plainText: 57 | return bytes() 58 | 59 | plainText = bytes(plainText) 60 | padLen = (blockSZ - len(plainText) % blockSZ) 61 | plainText = plainText + bytes([padLen]*padLen) 62 | 63 | cipherText = bytearray(blockSZ + len(plainText)) 64 | cipherText[0:blockSZ] = IV 65 | 66 | for start in range(blockSZ, len(cipherText), blockSZ): 67 | end = start + blockSZ 68 | cipherText[start:end] = self._cipher.encrypt( 69 | bytes(xor_block( 70 | islice(plainText, start-blockSZ, start), islice(cipherText, start-blockSZ, start)))) 71 | 72 | return cipherText 73 | 74 | def decrypt(self, cipherText): 75 | blockSZ = self.get_block_size() 76 | 77 | if not cipherText: 78 | return bytes() 79 | 80 | cipherText = bytes(cipherText) 81 | IV = islice(cipherText, blockSZ) 82 | 83 | ctWithPad = bytearray(chain.from_iterable( 84 | starmap(partial(map, operator.xor), ( 85 | (islice(cipherText, start-blockSZ, start), 86 | self._cipher.decrypt(cipherText[start:start+blockSZ])) 87 | for start in range(blockSZ, len(cipherText), blockSZ))))) 88 | 89 | ct = ctWithPad[0:-ctWithPad[-1]] 90 | return ct 91 | 92 | class CTRCipher(object): 93 | def __init__(self, blockCipher, key): 94 | self._cipher = blockCipher.new(key) 95 | 96 | def get_block_size(self): 97 | return self._cipher.block_size 98 | 99 | def encrypt(self, plainText, IV, catIV=True): 100 | blockSZ = self.get_block_size() 101 | 102 | IV = bytes(IV) 103 | if len(IV) != blockSZ: 104 | raise ValueError("Bad IV") 105 | 106 | if not plainText: 107 | return bytes() 108 | 109 | cipherIn = (bytes(b) for b in self._get_cipher_input_add(IV, len(plainText))) 110 | 111 | blocks = map(xor_block, 112 | map(self._cipher.encrypt, cipherIn), 113 | (islice(plainText, start, start+blockSZ) 114 | for start in range(0, len(plainText), blockSZ))) 115 | 116 | ct = bytes(chain.from_iterable(blocks)) 117 | 118 | if catIV: 119 | return IV + ct[:len(plainText)] 120 | else: 121 | return (IV, ct[:len(plainText)]) 122 | 123 | def decrypt(self, cipherText): 124 | blockSZ = self.get_block_size() 125 | IV, pt = self.encrypt(cipherText[blockSZ:], cipherText[:blockSZ], False) 126 | return pt 127 | 128 | def _combine_iv_ctr(self, IV, ctr): 129 | return map(operator.add, left, right) 130 | 131 | def _get_cipher_input_add(self, IV, msgLen): 132 | blockSZ = self.get_block_size() 133 | blocks = int((msgLen + blockSZ - 1)/blockSZ) 134 | return map(compose(reversed, partial(to_bytes, blockSZ)), 135 | starmap(operator.add, enumerate(repeat(from_bytes(reversed(IV)), blocks)))) 136 | 137 | # Looks like IV is used with add, not xor 138 | #def _get_cipher_input_xor(self, IV, msgLen): 139 | #blockSZ = self.get_block_size() 140 | #blocks = int((msgLen + blockSZ - 1)/blockSZ) 141 | #return map(bytes, 142 | #map(partial(map, operator.xor), 143 | #repeat(IV), (reversed(to_bytes(blockSZ, i)) for i in range(0, blocks)))) 144 | 145 | def self_test(): 146 | from binascii import unhexlify 147 | from binascii import hexlify 148 | tests = ( 149 | { 150 | "key": unhexlify("140b41b22a29beb4061bda66b6747e14"), 151 | "cipher": partial(CBCCipher, AES), 152 | "ciphertexts": map(unhexlify, ("4ca00ff4c898d61e1edbf1800618fb2828a226d160dad07883d04e008a7897ee2e4b7465d5290d0c0e6c6822236e1daafb94ffe0c5da05d9476be028ad7c1d81", "5b68629feb8606f9a6667670b75b38a5b4832d0f26e1ab7da33249de7d4afc48e713ac646ace36e872ad5fb8a512428a6e21364b0c374df45503473c5242a253")) 153 | }, 154 | { 155 | "key": unhexlify("36f18357be4dbd77f050515c73fcf9f2"), 156 | "cipher": partial(CTRCipher, AES), 157 | "ciphertexts": map(unhexlify, ("69dda8455c7dd4254bf353b773304eec0ec7702330098ce7f7520d1cbbb20fc388d1b0adb5054dbd7370849dbf0b88d393f252e764f1f5f7ad97ef79d59ce29f5f51eeca32eabedd9afa9329", "770b80259ec33beb2561358a9f2dc617e46218c0a53cbeca695ae45faa8952aa0e311bde9d4e01726d3184c34451")) 158 | } 159 | ) 160 | 161 | for test in tests: 162 | cipher = test["cipher"](test["key"]) 163 | for ct in test["ciphertexts"]: 164 | pt = cipher.decrypt(ct) 165 | ctFromPt = cipher.encrypt(pt, ct[:cipher.get_block_size()]) 166 | 167 | try: 168 | print("Decrypted {} -> {}".format(hexlify(ct[:5]), pt.decode('utf-8'))) 169 | except UnicodeDecodeError as e: 170 | print("ERROR: Bad decrypt. {}".format(e)) 171 | 172 | if ctFromPt != ct: 173 | print("ERROR: Round trip ciphertext -> plaintext -> re-ciphertext failed.") 174 | print("CT = {}".format(ct)) 175 | print("PT = {}".format(pt)) 176 | print("RT = {}".format(ctFromPt)) 177 | 178 | 179 | 180 | if __name__ == "__main__": 181 | self_test() 182 | -------------------------------------------------------------------------------- /ps5/solutions.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | 1. 3 | 4 | First, Alice contacts the TTP. At minimum, she must receive from the server 5 | the shared key encrypted with her own private key, or E(k_a, k_abc). 6 | 7 | She must also not send the shared key in the clear. Therefore the TTP should 8 | send Alice 9 | 10 | E(k_a, k_abc), ticket1 <- E(k_b, k_abc), ticket2 <- E(k_c, k_abc) 11 | Alice sends ticket1 to Bob and ticket2 to Carol 12 | 13 | -------------------------------------------------------------------------------- 14 | 2. 15 | 16 | > f := g^(x+y) 17 | This function _is not difficult to compute_. Specifically, an attacker can 18 | compute f by simply computing g^x * g^y = g^(x+y). 19 | 20 | > f := g^(x-y) 21 | This function _is not difficult to compute_. Specifically, an attacker can 22 | compute f by simply computing g^x/g^y = g^(x-y). 23 | 24 | > f := g^(xy + x + y + 1) 25 | This function _is difficult to compute_. Suppose an attacker can compute f. 26 | He could then compute DH with the equation 27 | DH(g^x, g^y) = g^xy = f(g^x, g^y)/(g * g^x * g^y) 28 | 29 | > f := g^2xy 30 | This function _is difficult to compute_. Suppose an attacker can compute f. 31 | He could then compute DH with the equation 32 | 33 | DH(g^x, g^y) = g^xy = sqrt_p(f(g^x, g^y)) 34 | Where sqrt is the square root modulo p, which is "easy" for a finite cyclic 35 | group Z_p 36 | 37 | > f := f^(x(y+1)) 38 | This function _is difficult to compute_. Suppose an attacker can compute f. 39 | He could then compute DH with the equation 40 | 41 | DH(g^x, g^y) = g^xy = f(g^x, g^y)/g^x 42 | 43 | > f := sqrt(g)^(x+y) 44 | This function _is not difficult_ to compute. Specifically, an attacker could 45 | compute f from sqrt(g^x * g^y) = f(g^x, g^y) 46 | 47 | -------------------------------------------------------------------------------- 48 | 3. 49 | 50 | Since Bob sends Alice g^(1/b), she has no way of computing g^ab. The secret 51 | key must therefore be g^(a/b). They generate this key as B^a and A^(1/b). In 52 | other words: 53 | 54 | _secret = g^(a/b). Alice computes the secret as B^a and Bob computes A^(1/b). 55 | 56 | -------------------------------------------------------------------------------- 57 | 4. 58 | 59 | The additional step _does not_ prevent the MitM attack. An attacker can 60 | intercept the initial message from Alice to Bob, and replace pk with pk'. He 61 | sends pk' to Bob, who responds with (E(pk', x), S(x, E(pk', x)). The attacker 62 | then decrypts E(pk', x) to determine x, computes E(pk, x), and sends 63 | (E(pk,x),S(x,E(pk,x)) to Alice. Alice sees that the MAC is correct and 64 | communicates insecurely with Bob. 65 | 66 | -------------------------------------------------------------------------------- 67 | 5. 68 | 69 | Using some simple python, we see that (a,b) = (10,-3) 70 | 71 | In Z_23, 7a + 23b = 7a and 1 = 1. 72 | So 7a = 1, or _7^-1 = 10_ 73 | 74 | -------------------------------------------------------------------------------- 75 | 6. 76 | 77 | 3x + 2 = 7 78 | 3x = 5 79 | 80 | x = 5 * (3^-1) = 5 * 13 = 65 mod 19 81 | = 8 82 | 83 | -------------------------------------------------------------------------------- 84 | 7. 85 | 86 | We aim to find |Z*_35| 87 | Since 35 = 7*5, |Z*_35| = 35 - 7 - 5 + 1 = 24 88 | 89 | -------------------------------------------------------------------------------- 90 | 8. 91 | 92 | We aim to find 2^(10001) mod 11 93 | Femat's theorem says that x^(p-1) = 1 in Zp. 94 | 95 | So consider the ring Z_11. 96 | x^(10) = 1 in Z_11 97 | 98 | We notice that 2^(10001) = 2*2^(10000) = 2*((((2^10)^10)^10)^10) = 2*1 = 99 | 2 in Z_11 100 | 101 | -------------------------------------------------------------------------------- 102 | 9. 103 | 104 | We aim to compute 2^245 mod 35 105 | 106 | Since 35 = 7*5, phi(35) = 24. 107 | 108 | 2^245 = 2^5 * 2^240 = 2^5 * (2^24)^10 109 | 110 | From Euler's theorem, we see that 2^24 = 2^phi(35) = 1 in Z_35 111 | 112 | So we have 2^245 = 2^5 * 1 = 113 | 32 in Z_35 114 | 115 | -------------------------------------------------------------------------------- 116 | 10. 117 | 118 | We aim to compute ord_35(2) 119 | By Lagranges theorem, ord_5(2) | 4 and ord_7(2) | 6. 120 | 121 | So ord_35(2) | 24. 122 | Clearly ord_2(35) > 5, so we try 6, 8, and 12. 123 | 124 | 2^6 = 64 = 29 in Z*_35 125 | 2^8 = 256 = 11 in Z*_35 126 | 2^12 = 4096 = 1 in Z*_35 127 | 128 | So ord_2(35) = 12 129 | 130 | -------------------------------------------------------------------------------- 131 | 11. 132 | 133 | The generated groups shown are not trickery. Since 13 is prime, all postive 134 | integers less than 13 are in Z*_13. So the generators are 2, 6, and 7. 135 | 136 | -------------------------------------------------------------------------------- 137 | 12. 138 | 139 | We aim to find the solution to x^2 + 4x + 1 = 0 in Z_23 140 | 141 | We use the formula 142 | 143 | x = (-b +- sqrt(b^2 - 4ac))/2a 144 | = (-b +- sqrt(b^2 -4ac) * (2a)^-1 145 | = (-4 +- sqrt(16 - 4)) * 2^-1 146 | = (-4 +- sqrt(12)) * 2^-1 147 | 148 | The inverse of 2 in Z*_23 can be found easily since 2^-1 in Z_N for any odd N 149 | is just (N+1)/2. Here N=23, so 2^-1 = 12. Thus we have 150 | 151 | = (-4 +- sqrt(12)) * 12 152 | 153 | Now since 23 = 3 mod 4, sqrt(12) can be computed using the simple relation 154 | sqrt(c) = c^((p+1)/4) in Zp where p is 23. So 155 | sqrt(12) = 12^(24/4) mod 23 = 12^6 mod 23 = 9 156 | So now we have 157 | 158 | x = (-4 +- 9) * 12 159 | = {5*12, -13*12} = {60, -156} 160 | = {14, 5} in Z_23 161 | 162 | We can verify easily: 163 | 14^2 + 4*14 + 1 = 253 = 23*11 164 | 5^2 + 4*5 + 1 = 46 = 23*2 165 | 166 | -------------------------------------------------------------------------------- 167 | 13. 168 | 169 | We aim to find 2^(1/11) in Z_19. 170 | 171 | We have a theorem that says when gcd(e, p-1) = 1, then c^(1/e) is c^(e^-1) 172 | Fortunately, 11 and 18 are coprime. That is, gcd(11, 18) = 1. 173 | 174 | So 2^(1/11) = 2^(d), where d = the inverse of 11 in Z_18 175 | 176 | The inverse of 11 in Z_18 can be found efficiently using euclid's algorithm. 177 | We compute 11^-1 = 5 178 | 179 | so the 11th root of 2 in Z_19 is 2^5 180 | = 13 in Z_19. 181 | 182 | -------------------------------------------------------------------------------- 183 | 14. 184 | 185 | We aim to find Dlog_2(5) in Z_13. 186 | 187 | That is, we aim to find x so that 5 = 2^x in Z_13. 188 | 189 | From inspection of the generated group <2>, we see that 2^9 = 5 in Z_13, so 190 | Dlog_2(5) = 9 in Z_13. 191 | 192 | -------------------------------------------------------------------------------- 193 | 15. 194 | 195 | Let p be a prime number. 196 | 197 | An element g in Z*_p is a generator if || = |Z*_p|. 198 | The set of generators of Z*_p is precisely the set {g : || = |Z*_p| = p-1} 199 | 200 | TODO: Finish 201 | phi(p-1) 202 | 203 | -------------------------------------------------------------------------------- 204 | -------------------------------------------------------------------------------- /ps4/solutions.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | 1. 3 | 4 | We receive the following ciphertext. 5 | 6 | 20814804c1767293b99f1d9cab3bc3e7 ac1e37bfb15599e5f40eef805488281d 7 | 8 | The first 16 bytes are the IV. We see that on decryption, the IV will be 9 | XORed with the decryption of the first message block. We simply identify the 10 | position of the number '1' in "Pay Bob 100$", 11 | 12 | 20814804c1767293b99f1d9cab3bc3e7 13 | P A Y B O B 1 0 0 $ 14 | 15 | then we replace the '1' with a '5' by XORing the correct positon with ord('1') 16 | ^ ord('5') = 0x04, where ord(x) is the ASCII encoding of x. 17 | 18 | 20814804c1767293b99f1d9cab3bc3e7 19 | xor 00000000000000000400000000000000 20 | ____________________________________ 21 | =20814804c1767293bd9f1d9cab3bc3e7 22 | 23 | So the resulting ciphertext is 24 | _20814804c1767293bd9f1d9cab3bc3e7 ac1e37bfb15599e5f40eef805488281d_ 25 | 26 | -------------------------------------------------------------------------------- 27 | 2. 28 | 29 | > E(k2, E(k1,m)) and ( D(k2,c) == bottom ? bottom : D(k1, D(k2,c)) 30 | This system _does provide AE_. Suppose an attacker could for a c that does 31 | not result in D' output bottom. Then the attacker could could use the 32 | same strategy against (E,D) with by encrypting outgoing messages with k2 and 33 | decrypting incoming messages with k2. Similarly, if the attacker could mount 34 | a CPA against (E',D'), he could use the same attack to mount a CPA against 35 | (E,D) by encrypting and decrypting with k2 before sending or receiving data. 36 | 37 | > (E(k,m), 0) and D'(k (c,b)) = b == 0 ? D(k,c) : bottom 38 | This system _does provide AE_. An attacker who can mount a CPA or existential 39 | forgery on (E',D') can use the same method on (E,D). He can send messages to 40 | E, pad them with 0. Any ciphertext he submits must have b==0 or else D' would 41 | have output bottom, so he can simply remove the appended zeros from his 42 | forgery from D' to create a forgery for D. The process is similar for a CPA 43 | attack. 44 | 45 | > E'=E and D' = D if D != bottom, or 0^n otherwise. 46 | This system _does not provide AE_. An attacker can submit any ciphertext 47 | without receiving bottom in return, so his advantage in the integrity game is 48 | 1 (and he doesn't even have to submit any messages for encryption by the 49 | challenger). 50 | 51 | > (E(k,m), E(k,m)) and D'(k, (c1,c1)) = D(k,c1) 52 | This system _does not provide AE_. An attacker can mount the following 53 | integrity attack. He first asks for the encryption of some arbitrary m, for 54 | which he receives (c, c). He then sends the ciphertext (c, ~c). Since (c, ~c) is 55 | not equal to (c, c) and D'(k, (c, ~c)) = D(k, c) = m (not bottom), the 56 | attacker has successfully forged a valid ciphertext. 57 | 58 | > E'=(E(k,m), E(k,m)) and D'(k, (c1,c2)) = c1 == c2 ? D(k, c1) : bottom 59 | This system _does provide AE_. 60 | Any forgery on (E',D') is also a forgery for (E,D). Any CPA on (E',D') can be 61 | converted into a CPA on (E,D) by simply duplicating ciphertexts from E(k,m) 62 | and passing them to the (E',D') adversary. 63 | 64 | > (E(k,m), E(k,m)) and D' = {D(k,c1)==D(k,c2) ? D(k,c1) : bottom} 65 | This system _does not provide AE_. E' will output distinct (c1,c2) since E 66 | must be randomized to be AE. Thus the adversary can forge (c1,c1) and win the 67 | integrity game. 68 | 69 | -------------------------------------------------------------------------------- 70 | 3. 71 | 72 | This question seems more like a reaffirmation of the lecture contents than a 73 | real question. Any response that includes "implement ... yourself" or "invent 74 | your own" is clearly wrong. Also, just using CBC encryption does not provide 75 | message integrity. Therefore the correct answer is to _use a standard 76 | implementation of one of the authenticated encryption modes GCM, CCM, EAX, or 77 | OCB_. 78 | 79 | -------------------------------------------------------------------------------- 80 | 4. 81 | 82 | In order for (E,D) to create a secure MAC, it must not be possible for an 83 | attacker to create a forgery. Chosen ciphertext security isn't enough. Need 84 | Authenticated Encryption. 85 | 86 | -------------------------------------------------------------------------------- 87 | 5. 88 | 89 | When the key is sampled from the non-uniform distribution, it will always be 90 | the case that MSB_128(c) = 0^128. Any PRF that becomes non-random for inputs 91 | with MSB_128(c) = 0^128 is insecure when taking inputs from the distribution. 92 | 93 | The PRF _F'(k,x) = MSB_128(c) != 0^128 ? F(k,x) : 1^256_ will almost always 94 | evaluate to F(k,x), as long as k is sampled uniformly over K. However, when k 95 | is sampled only from the subset of K where the most significant 128 bits are 96 | zero, F' will always evaluate to 1^256 and will thus be insecure. 97 | 98 | -------------------------------------------------------------------------------- 99 | 6. 100 | 101 | DAE can be used whenever messages are very likely to be unique. It shouldn't 102 | be used if there is a chance that identical messages will be encrypted. 103 | 104 | -------------------------------------------------------------------------------- 105 | 7. 106 | 107 | Let E(k,x) be a secure block cipher and E'((k1,k2),t,x) = E(k1,x) xor E(k2,t) 108 | 109 | A tweakable block cipher is insecure if an adversary can distinguish the 110 | cipher from a random permutation in the tweak space. 111 | 112 | E' is not secure. An adversary can request the encryptions of 113 | 114 | a. [0](m1) -> E(k1,m1) xor E(k2,0) 115 | b. [1](m1) -> E(k1,m1) xor E(k2,1) 116 | c. [0](m2) -> E(k1,m2) xor E(k2,0) 117 | d. [1](m2) -> E(k1,m2) xor E(k2,1) 118 | 119 | He then computes 120 | 121 | a ^ b = E(k2,0) ^ E(k2,1) 122 | c ^ d = E(k2,0) ^ E(k2,1) 123 | 124 | The attacker checks that a^b==c^d. With very high probability, he will 125 | determine whether or not the challenger is using E'. Therefore his advantage 126 | in the tweakable CPA game is ~1 and E' is not secure. 127 | 128 | -------------------------------------------------------------------------------- 129 | 8. 130 | 131 | Each iteration is an independent, identically distributed bernoulli random 132 | variable, so the number of steps in the whole algorithm is a geometric random 133 | variable. The probability of "success" for each trial is p = 10^16/2^128, so 134 | the expected number of trials is 1/p = _2^128/10^16_. 135 | 136 | -------------------------------------------------------------------------------- 137 | 9. 138 | 139 | The MAC _is secure_. Suppose an adversary could forge a message tag pair 140 | (m,t). He could then mount attack (E,D) by making the requests needed to find 141 | (m,t), then asking for the encryption of [m](0). If E(k, m, 0) == t, then the 142 | adversary knows that the challenger is using E instead of a random function. 143 | 144 | -------------------------------------------------------------------------------- 145 | 10. 146 | 147 | A padding oracle guesses all 256 possibilities of each byte's value for each 148 | byte. In the worst case an attacker would ahve to make 256*48 = _12288_. 149 | 150 | -------------------------------------------------------------------------------- 151 | --------------------------------------------------------------------------------