Cryptopals Challenges Set 2 - Solutions - LostMyPlaintext

Challenge 9:

import binascii

BLOCK_SIZE = 16

def PKCS7_padding(s):
	if len(s)%BLOCK_SIZE != 0:
		return s + chr((BLOCK_SIZE*(len(s)//BLOCK_SIZE)+BLOCK_SIZE)-len(s)).encode()*((BLOCK_SIZE*(len(s)//BLOCK_SIZE + 1))-len(s))
	return s

def main():
	pt = b"YELLOW SUBMARINE"
	print (PKCS7_padding(pt))

if __name__ == "__main__":
	main()

Challenge 10:

from Crypto.Cipher import AES
import base64

def cbc_decrypt(ct,passphrase,iv):
	aes = AES.new(passphrase, AES.MODE_CBC, iv)
	return aes.decrypt(base64.b64decode(ct))

def main():
	with open("10.txt","r") as file:
		s = "".join(line[:-1] for line in file)
	passphrase = b"YELLOW SUBMARINE"
	iv = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
	print (cbc_decrypt(s,passphrase,iv))

if __name__ == "__main__":
	main()

Challenge 11:

from Crypto.Cipher import AES
from os import urandom
from random import randint
from base64 import b64encode, b64decode

KEY_SIZE = 16
IV_SIZE = 16
BLOCK_SIZE = 16
PLAINTEXT_SIZE = 1337

def genkey(size):
	return urandom(size)

def appendbytes(pt):
	prefix = randint(5,10)
	suffix = randint(5,10)
	return urandom(prefix) + pt + urandom(suffix)

def genIV():
	return urandom(IV_SIZE)

def PKCS7_padding(s):
	if len(s)%BLOCK_SIZE != 0:
		return s + chr((BLOCK_SIZE*(len(s)//BLOCK_SIZE)+BLOCK_SIZE)-len(s)).encode()*((BLOCK_SIZE*(len(s)//BLOCK_SIZE + 1))-len(s))
	return s

def encrypt(pt):
	key = genkey(KEY_SIZE)
	pt = PKCS7_padding(pt)
	iv = genIV()
	if randint(0,1) == 0:
		aes = AES.new(key, AES.MODE_ECB)
		mode = "ECB"
	else:
		aes = AES.new(key, AES.MODE_CBC, iv)
		mode = "CBC"
	ct = b64encode(aes.encrypt(pt))
	return (ct,mode)

def AES_ECB_Score(ct):
	ct = b64decode(ct)
	blocks = [ct[i:i + BLOCK_SIZE] for i in range(0, len(ct), BLOCK_SIZE)]
	score = len(blocks) - len(set(blocks))
	return score

def main():
	pt = appendbytes(urandom(PLAINTEXT_SIZE))
	ct = encrypt(pt+b"A"*1337)
	print ("Ciphertext > " + str(ct[0]) + "\nAES mode used > " + str(ct[1]))
	if AES_ECB_Score(ct[0]) > 0:
		print ("Guessed mode > ECB")
	else:
		print ("Guessed mode > CBC")

if __name__ == '__main__':
	main()

Challenge 12:

from Crypto.Cipher import AES
from os import urandom

import base64
import string

KEY_SIZE = 16
IV_SIZE = 16
BLOCK_SIZE = 16
HOW_MANY_BLOCKS = 9
PT_LEN = HOW_MANY_BLOCKS * BLOCK_SIZE

def PKCS7_padding(s):
	if len(s)%BLOCK_SIZE != 0:
		return s + chr((BLOCK_SIZE*(len(s)//BLOCK_SIZE)+BLOCK_SIZE)-len(s)).encode()*((BLOCK_SIZE*(len(s)//BLOCK_SIZE + 1))-len(s))
	return s

def enc(pt,key):
	pt = PKCS7_padding(pt)
	aes = AES.new(key, AES.MODE_ECB)
	ct = aes.encrypt(pt)
	return ct

def brute_force_ECB(pt,key):
	known_bytes = b""
	for i in range(0,PT_LEN):
		cur = enc(b'A'*(PT_LEN - 1  - i) + pt,key)[:(PT_LEN -1 )]
		for byte in string.printable.encode():
			if enc(b'A'*(PT_LEN - 1  - i) + known_bytes + chr(byte).encode(),key)[:(PT_LEN-1)] == cur:
				known_bytes += chr(byte).encode()
				break
	return known_bytes.decode()

def main():
	pt = base64.b64decode('''Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkgaGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBqdXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUgYnkK''')
	key = urandom(KEY_SIZE)
	print (brute_force_ECB(pt,key))

if __name__ == '__main__':
	main()

Challenge 13:

from Crypto.Cipher import AES
from os import urandom

import base64
import random
import re
import sys

BLOCK_SIZE =16
KEY_SIZE = 16

key = urandom(KEY_SIZE)

def PKCS7_padding(s):
	if len(s)%BLOCK_SIZE != 0:
		return s + chr((BLOCK_SIZE*(len(s)//BLOCK_SIZE)+BLOCK_SIZE)-len(s))*((BLOCK_SIZE*(len(s)//BLOCK_SIZE + 1))-len(s))
	return s

def enc(pt):
	pt = PKCS7_padding(pt)
	aes = AES.new(key, AES.MODE_ECB)
	return aes.encrypt(pt.encode())


def dec(ct):
	aes = AES.new(key, AES.MODE_ECB)
	return aes.decrypt(ct).decode()

def parser(cookie):
	parts = cookie.split("&")
	dict = {}
	for part in parts:
		key_value = part.split("=")
		dict[key_value[0]] = key_value[1]
	return dict


def profile(email):
	if re.match(".[&=].",email):
		return "Invalid characters"
	profile = {
		'email':email,
		'uid':1337,
		'role':'user',
	}
	ret = "email={}&uid={}&role={}".format(profile["email"],profile["uid"],profile["role"])
	return ret

def ECB_cut_and_paste(profile,newrole):
	return  dec(enc(profile)[:32] + enc("admin"))[:-11]

def main():
	email = "42@mail.com"
	#Since len(email) = 11 the second block of enc(profile(email)) will end in "role="
	#To create a valid admin profile we'll take the first 2 blocks of enc(profile(email)) and concat enc("admin")
	#The decrypted result will be a valid admin profile
	#block1			 	block2          	block3
	#email=42@mail.co	m&uid=1337&role= 	admin(padding)
	admin_profile =  parser(ECB_cut_and_paste(profile(email),"admin"))
	print ("Registered the email > {}\nCorrespondent uid > {}\nRespective role > {}".format(admin_profile["email"],admin_profile["uid"],admin_profile["role"]))

if __name__ == '__main__':
	main()

Challenge 14:

from Crypto.Cipher import AES
from os import urandom
from random import randint

import base64
import string

KEY_SIZE = 16
IV_SIZE = 16
BLOCK_SIZE = 16
HOW_MANY_BLOCKS = 9
PT_LEN = HOW_MANY_BLOCKS * BLOCK_SIZE

def PKCS7_padding(s):
	if len(s)%BLOCK_SIZE != 0:
		return s + chr((BLOCK_SIZE*(len(s)//BLOCK_SIZE)+BLOCK_SIZE)-len(s)).encode()*((BLOCK_SIZE*(len(s)//BLOCK_SIZE + 1))-len(s))
	return s

def enc(pt,key):
	pt = PKCS7_padding(pt)
	aes = AES.new(key, AES.MODE_ECB)
	ct = aes.encrypt(pt)
	return ct

def brute_force_ECB(pt,key):
	known_bytes = b""
	flag = enc(b"STIMULUSRESPONSE",key)
	for i in range(0,PT_LEN):
		cur_try = b""
		while True:
			cur_try = enc(urandom(randint(0,255)) + b"STIMULUSRESPONSE" + b"A"*(PT_LEN - 1 - i) + pt,key)
			if flag in cur_try:
				break
		for byte in string.printable.encode():
			while True:
				cur_byte = enc(urandom(randint(0,255)) + b"STIMULUSRESPONSE" + b"A"*(PT_LEN-1-i) + known_bytes  + chr(byte).encode(),key)
				if flag in cur_byte:
					break
			if cur_byte.split(flag)[1][:PT_LEN - 1] == cur_try.split(flag)[1][:PT_LEN - 1]:
				known_bytes += chr(byte).encode()
				break

	return known_bytes.decode()

def main():
	pt = base64.b64decode('''Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkgaGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBqdXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUgYnkK''')
	key = urandom(KEY_SIZE)
	print (brute_force_ECB(pt,key))

if __name__ == '__main__':
	main()

Challenge 15:

BLOCK_SIZE = 16

def PKCS7_padding_validation(pt):
	if len(pt)%BLOCK_SIZE != 0:
		return "Invalid padding"
	else:
		if pt[len(pt)-1] >= BLOCK_SIZE:
			return (b"Valid padding > " + pt).decode()

		elif chr(pt[len(pt)-1]).encode()*(pt[len(pt)-1]) == pt[-pt[len(pt)-1]:]:
			return (b"Valid padding, plaintext > " + pt).decode()
		return "Invalid padding!"


def main():
	pt = b"YELLOW SUBMARINE" 
	print (PKCS7_padding_validation(pt))

if __name__ == "__main__":
	main()

Challenge 16:

from Crypto.Cipher import AES
from os import urandom

BLOCK_SIZE = 16
KEY_SIZE = 16
IV_SIZE = 16

key = urandom(KEY_SIZE)
iv = urandom(IV_SIZE)

prefix = b"comment1=cooking%20MCs;userdata="
suffix = b";comment2=%20like%20a%20pound%20of%20bacon" 

def filter_and_pad(pt):
	pt = pt.replace(b";",b"%").replace(b"=",b"%")
	return prefix + pt + suffix

def PKCS7_padding(s):
	if len(s)%BLOCK_SIZE != 0:
		return s + chr((BLOCK_SIZE*(len(s)//BLOCK_SIZE)+BLOCK_SIZE)-len(s)).encode()*((BLOCK_SIZE*(len(s)//BLOCK_SIZE + 1))-len(s))
	return s

def encrypt(pt):
	pt = PKCS7_padding(pt)
	aes = AES.new(key, AES.MODE_CBC, iv)
	ct = aes.encrypt(pt)
	return ct

def cbc_decrypt(ct):
	aes = AES.new(key, AES.MODE_CBC, iv)
	dec = aes.decrypt(ct)
	if b";admin=true;" in dec:
		return True
	return False

def CBC_bitflipping_attack(ct):
	semicolon = ct[len(prefix)-16] ^ ord("%") ^ ord(";")
	equals = ct[len(prefix)-10] ^ ord("%") ^ ord("=")
	return ct[:len(prefix) - 16] + bytes([semicolon]) + ct[len(prefix)-15:len(prefix) - 10] + bytes([equals]) + ct[len(prefix) - 9:]

def main():
	pt = b";admin=true"
	ct = encrypt(filter_and_pad(pt))
	alt_ct = (CBC_bitflipping_attack(ct))
	print (cbc_decrypt(alt_ct))

if __name__ == "__main__":
	main()