# Alexander HanelCryptoPals

## Set 1

### Challenge 1 - Convert hex to base64

```import base64
base64.b64encode("49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d".decode("hex"))
```

### Challenge 2 - Fixed XOR

```from itertools import cycle

def xor_mb(message, key):
return''.join(chr(ord(m_byte)^ord(k_byte)) for m_byte,k_byte in zip(message, cycle(key)))

xor_mb("1c0111001f010100061a024b53535009181c".decode('hex'), "686974207468652062756c6c277320657965".decode('hex')).encode('hex')
```

### Challenge 3 - Single-byte XOR cipher

```from collections import Counter
message = "1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736".decode('hex')
cnt = Counter(message)
cnt.most_common(3)
for vv in cnt.most_common(7):
for char in "ETAOIN SHRDLU".lower():
key = chr(ord(vv[0]) ^ ord(char))
print key, xor_mb(message, key)
```

### Challenge 4 - Detect single-character XOR

• Solution using limited frequency analysis approach
```import string
from collections import Counter
from itertools import cycle

def xor_mb(message, key):
return''.join(chr(ord(m_byte)^ord(k_byte)) for m_byte,k_byte in zip(message, cycle(key)))

printset = set(string.printable)
messages = open("4.txt", 'r').readlines()

for line in messages:
message = line.rstrip().decode('hex')
cnt = Counter(message)
for vv in cnt.most_common(3):
for char in "ETAOINSHRDLU".lower():
key = chr(ord(vv[0]) ^ ord(char))
temp = xor_mb(message, key)
if set(temp).issubset(printset) and temp[-1] == "\n":
print key, temp
```

chi-squared approach

```import math
import string

from collections import Counter
from itertools import cycle

# Source http://jsbin.com/kaxoxajige/1/edit?js,output
expected = [0.08167,0.01492,0.02782,0.04253,0.12702,0.02228,0.02015,0.06094,0.06966,0.00153,0.00772,
0.04025,0.02406,0.06749,0.07507,0.01929,0.00095,0.05987,0.06327,0.09056,0.02758,0.00978,
0.02360,0.00150,0.01974,0.00074]

# Source -h https://hflog.wordpress.com/2014/04/01/how-to-perform-a-chi-squared-goodness-of-fit-test-in-python

def gf(x):
#Play with these values to adjust the error of the approximation.
upper_bound=100.0
resolution=1000000.0
step=upper_bound/resolution
val=0
rolling_sum=0
while val<=upper_bound:
rolling_sum+=step*(val**(x-1)*2.7182818284590452353602874713526624977**(-val))
val+=step
return rolling_sum

def ilgf(s,z):
val=0
for k in range(0,100):
val+=(((-1)**k)*z**(s+k))/(math.factorial(k)*(s+k))
return val

def chisquarecdf(x,k):
return 1-ilgf(k/2,x/2)/gf(k/2)

def chisquare(observed_values,expected_values):
test_statistic=0
for observed, expected in zip(observed_values, expected_values):
test_statistic+=(float(observed)-float(expected))**2/float(expected)
df=len(observed_values)-1
return test_statistic, chisquarecdf(test_statistic,df)

def frequency(message):
message = message.lower().strip(" ")
freq = []
length = len(message)
for char in "abcdefghijklmnopqrstuvwxyz":
freq.append(float(message.count(char)) / length)
return freq

def xor_mb(message, key):
return''.join(chr(ord(m_byte)^ord(k_byte)) for m_byte,k_byte in zip(message, cycle(key)))

printset = set(string.printable)
messages = open("4.txt", 'r').readlines()

for line in messages:
message = line.rstrip().decode('hex')
for key in range(0 ,256):
temp = xor_mb(message, chr(key))
temp_freq = frequency(temp)
chi, tt = chisquare(temp_freq, expected)
print chi, tt, temp
```

### Challenge 5 - Implement repeating-key XOR

``` def xor_mb(message, key):
return''.join(chr(ord(m_byte)^ord(k_byte)) for m_byte,k_byte in zip(message, cycle(key)))

plain = """Burning 'em, if you ain't quick and nimble
I go crazy when I hear a cymbal"""
xor_mb(plain, "ICE").encode('hex')
```

### Challenge 6 - Break repeating-key XOR

• First part uses Hamming distance to calcualte the XOR key size.
• I think this is pretty slick. The code below can be used to crack locky encoded executables.
```import base64
import string
import sys
import collections

from cStringIO import StringIO
from collections import Counter
from itertools import cycle
from itertools import product

def xor_mb(message, key):
return''.join(chr(ord(m_byte)^ord(k_byte)) for m_byte,k_byte in zip(message, cycle(key)))

def hamming_distance(bytes_a, bytes_b):
return sum(bin(i ^ j).count("1") for i, j in zip(bytearray(bytes_a), bytearray(bytes_b)))

def key_len(message, key_size):
""""returns [(dist, key_size),(dist, key_size)]"""
avg = []
for k in xrange(2,key_size):
hd = []
for n in xrange(len(message)/k-1):
hd.append(hamming_distance(message[k*n:k*(n+1)],message[k*(n+1):k*(n*2)])/k)
if hd:
avg.append((sum(hd) / float(len(hd)), k))
return sorted(avg)[:10]

d_data = base64.b64decode(data)
print key_len(d_data, 64)
```

### Challenge 7 - AES in ECB mode

```import base64
from Crypto.Cipher import AES

key = "YELLOW SUBMARINE"
IV = 24 * "\x00"
mode = AES.MODE_ECB
message =  base64.b64decode(open("7.txt", "rb").read())
encryptor = AES.new(key, mode, IV=IV)
plain = encryptor.decrypt(message)
print plain
```

### Challenge 8 - Detect AES in ECB mode

```from collections import Counter

KEY_SIZE = 16
data = open("8.txt", "r").readlines()
for line in data:
data = line.rstrip().decode('hex')
substr_counter = Counter(data[i: i+ KEY_SIZE] for i in range(len(data) - KEY_SIZE))
sub_count = substr_counter.most_common(5)
for temp in sub_count:
key, count = temp
if count > 3:
print key, data
```

### Challenge 9 - Implement PKCS#7 padding

```BLOCK_SIZE = 20 # not correct standard size