Cryptopals Challenges Set 3 - Solutions - LostMyPlaintext

Challenge 17:

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

import string

BLOCKSIZE = 16

def getRandomString():
	strings = ["MDAwMDAwTm93IHRoYXQgdGhlIHBhcnR5IGlzIGp1bXBpbmc=","MDAwMDAxV2l0aCB0aGUgYmFzcyBraWNrZWQgaW4gYW5kIHRoZSBWZWdhJ3MgYXJlIHB1bXBpbic=","MDAwMDAyUXVpY2sgdG8gdGhlIHBvaW50LCB0byB0aGUgcG9pbnQsIG5vIGZha2luZw==","MDAwMDAzQ29va2luZyBNQydzIGxpa2UgYSBwb3VuZCBvZiBiYWNvbg==","MDAwMDA0QnVybmluZyAnZW0sIGlmIHlvdSBhaW4ndCBxdWljayBhbmQgbmltYmxl","MDAwMDA1SSBnbyBjcmF6eSB3aGVuIEkgaGVhciBhIGN5bWJhbA==","MDAwMDA2QW5kIGEgaGlnaCBoYXQgd2l0aCBhIHNvdXBlZCB1cCB0ZW1wbw==","MDAwMDA3SSdtIG9uIGEgcm9sbCwgaXQncyB0aW1lIHRvIGdvIHNvbG8=","MDAwMDA4b2xsaW4nIGluIG15IGZpdmUgcG9pbnQgb2g=","MDAwMDA5aXRoIG15IHJhZy10b3AgZG93biBzbyBteSBoYWlyIGNhbiBibG93"]
	return b64decode(strings[randint(0,len(strings)-1)])


def padding(string, blockSize):
	if len(string)%blockSize == 0:
		return string + b'\x0f'*16
	byte = blockSize-(len(string)%blockSize)
	pad = b"".join( bytes.fromhex( hex(byte)[2:].rjust(2,"0") ) for _ in range(byte))
	return string + pad


def PKCS7paddingValidation(string, blocksize):
	byteNum = string[len(string)-1]
	toVerify = string[-byteNum:]
	if toVerify == chr(byteNum).encode()*byteNum:
		return True
	return False

# Retrieve the padding used in the orignal plaintext
def getOriginalPadding(lastBlock, key, previousBlock):
	editBlock = bytearray(previousBlock)
	ret = 0
	while cbcDecrypt(lastBlock, key, editBlock):
		editBlock[ret+1] = 255
		ret += 1
	return chr(BLOCKSIZE - ret).encode()

# Get list of ciphertext blocks
def getBlocks(ciphertext):
	ret = []
	for i in range(0,len(ciphertext)//BLOCKSIZE):
		ret.append(ciphertext[i*BLOCKSIZE:(i+1)*BLOCKSIZE])
	return ret


def cbcEncrypt(plaintext, key, iv):
	plaintext = padding(plaintext, BLOCKSIZE)
	aes = AES.new(key, AES.MODE_CBC, iv)
	ciphertext = aes.encrypt(plaintext)
	return ciphertext


def cbcDecrypt(ciphertext, key, iv):
	aes = AES.new(key, AES.MODE_CBC, iv)
	plaintext = aes.decrypt(ciphertext)
	return PKCS7paddingValidation(plaintext, BLOCKSIZE)


def cbcPaddingOracleAttack(ciphertext, key, iv):
	plaintext = b""
	blocks = getBlocks(ciphertext)
	blocks.insert(0,iv)

	# We'll need this later for solving ambiguities
	originalPadding = getOriginalPadding(blocks[ len(blocks)-1  ], key, blocks[ len(blocks)-2  ])
	print ("Original Paddind:",originalPadding)

	for i in range(0, len(blocks)-1):

		chunk = b""

		for j in range(0,BLOCKSIZE):
			curBlock = bytearray(blocks[i])
			if len(chunk) != 0:
				tmp = BLOCKSIZE - 1
				for ch in range(0, len(chunk)):
					curBlock[tmp] = (len(chunk)+1) ^ chunk[ch] ^ curBlock[tmp]
					tmp -= 1

			options = []
			for k in range(0,256):
				curBlock[len(curBlock)-1-j] = k
				if cbcDecrypt( bytearray(blocks[i+1]), key, curBlock):
					options.append(k)

			if len(options) == 1:
				chunk += chr( options[0] ^ (len(chunk)+1) ^ blocks[i][BLOCKSIZE-1-j] ).encode()
			else:
				nextChar = b""
				for op in options:
						curChar = chr( op ^ (len(chunk)+1) ^ blocks[i][BLOCKSIZE-1-j] ).encode()
						if curChar == originalPadding:
							nextChar = curChar
				chunk += nextChar

		plaintext += chunk[::-1]

	return plaintext


def main():

	key = urandom(BLOCKSIZE)
	iv = urandom(BLOCKSIZE)

	string = getRandomString()
	ciphertext = cbcEncrypt(string, key, iv)
	plaintext = cbcPaddingOracleAttack(ciphertext, key, iv)
	print ("Original plaintext:", string)
	print ("Decryption result:", plaintext)

if __name__ == "__main__":
	main()

Challenge 18:

from Crypto.Cipher import AES
from Crypto.Util import Counter

import base64

BLOCKSIZE = 16

def xorStrings(data0, data1):
	ret = b"".join(bytes.fromhex(hex(data0[i]^data1[i])[2:].rjust(2,'0')) for i in range(min(len(data0),len(data1))))
	return ret

def encrypt(plaintext, key, nonce):
	aes = AES.new(key, AES.MODE_ECB)
	counter = 0
	keyStream = b""
	keyLen = (len(plaintext)//BLOCKSIZE) + 1 if len(plaintext)%16 != 0 else (len(plantext)//BLOCKSIZE)
	for i in range(0, keyLen):
		curCounter = bytes.fromhex(hex(counter)[2:(BLOCKSIZE+2)].rjust(BLOCKSIZE,'0'))[::-1]
		keyStream += aes.encrypt(nonce + curCounter)
		counter += 1
	return xorStrings(plaintext, keyStream)

def decrypt(ciphertext, key, nonce):
	aes = AES.new(key, AES.MODE_ECB)
	counter = 0
	keyStream = b""
	keyLen = (len(ciphertext)//BLOCKSIZE) + 1 if len(ciphertext)%16 != 0 else (len(ciphertext)//BLOCKSIZE)
	for i in range(0, keyLen):
		curCounter = bytes.fromhex(hex(counter)[2:(BLOCKSIZE+2)].rjust(BLOCKSIZE,'0'))[::-1]
		keyStream += aes.encrypt(nonce + curCounter)
		counter += 1
	return xorStrings(ciphertext, keyStream)

def main():
	key = b"YELLOW SUBMARINE"
	nonce = b'\x00'*(BLOCKSIZE//2)
	ciphertext = base64.b64decode("L77na/nrFsKvynd6HzOoG7GHTLXsTVu9qvY/2syLXzhPweyyMTJULu/6/kXX0KSvoOLSFQ==")
	plaintext = decrypt(ciphertext, key, nonce)
	print ( plaintext.decode() )

if __name__ == "__main__":
	main()

Challenge 19:

from Crypto.Cipher import AES
from os import urandom

import base64

BLOCKSIZE = 16

plaintexts = ['SSBoYXZlIG1ldCB0aGVtIGF0IGNsb3NlIG9mIGRheQ==', 'Q29taW5nIHdpdGggdml2aWQgZmFjZXM=', 'RnJvbSBjb3VudGVyIG9yIGRlc2sgYW1vbmcgZ3JleQ==', 'RWlnaHRlZW50aC1jZW50dXJ5IGhvdXNlcy4=', 'SSBoYXZlIHBhc3NlZCB3aXRoIGEgbm9kIG9mIHRoZSBoZWFk', 'T3IgcG9saXRlIG1lYW5pbmdsZXNzIHdvcmRzLA==', 'T3IgaGF2ZSBsaW5nZXJlZCBhd2hpbGUgYW5kIHNhaWQ=', 'UG9saXRlIG1lYW5pbmdsZXNzIHdvcmRzLA==', 'QW5kIHRob3VnaHQgYmVmb3JlIEkgaGFkIGRvbmU=', 'T2YgYSBtb2NraW5nIHRhbGUgb3IgYSBnaWJl', 'VG8gcGxlYXNlIGEgY29tcGFuaW9u', 'QXJvdW5kIHRoZSBmaXJlIGF0IHRoZSBjbHViLA==', 'QmVpbmcgY2VydGFpbiB0aGF0IHRoZXkgYW5kIEk=', 'QnV0IGxpdmVkIHdoZXJlIG1vdGxleSBpcyB3b3JuOg==', 'QWxsIGNoYW5nZWQsIGNoYW5nZWQgdXR0ZXJseTo=', 'QSB0ZXJyaWJsZSBiZWF1dHkgaXMgYm9ybi4=', 'VGhhdCB3b21hbidzIGRheXMgd2VyZSBzcGVudA==', 'SW4gaWdub3JhbnQgZ29vZCB3aWxsLA==', 'SGVyIG5pZ2h0cyBpbiBhcmd1bWVudA==', 'VW50aWwgaGVyIHZvaWNlIGdyZXcgc2hyaWxsLg==', 'V2hhdCB2b2ljZSBtb3JlIHN3ZWV0IHRoYW4gaGVycw==', 'V2hlbiB5b3VuZyBhbmQgYmVhdXRpZnVsLA==', 'U2hlIHJvZGUgdG8gaGFycmllcnM/', 'VGhpcyBtYW4gaGFkIGtlcHQgYSBzY2hvb2w=', 'QW5kIHJvZGUgb3VyIHdpbmdlZCBob3JzZS4=', 'VGhpcyBvdGhlciBoaXMgaGVscGVyIGFuZCBmcmllbmQ=', 'V2FzIGNvbWluZyBpbnRvIGhpcyBmb3JjZTs=', 'SGUgbWlnaHQgaGF2ZSB3b24gZmFtZSBpbiB0aGUgZW5kLA==', 'U28gc2Vuc2l0aXZlIGhpcyBuYXR1cmUgc2VlbWVkLA==', 'U28gZGFyaW5nIGFuZCBzd2VldCBoaXMgdGhvdWdodC4=', 'VGhpcyBvdGhlciBtYW4gSSBoYWQgZHJlYW1lZA==', 'QSBkcnVua2VuLCB2YWluLWdsb3Jpb3VzIGxvdXQu', 'SGUgaGFkIGRvbmUgbW9zdCBiaXR0ZXIgd3Jvbmc=', 'VG8gc29tZSB3aG8gYXJlIG5lYXIgbXkgaGVhcnQs', 'WWV0IEkgbnVtYmVyIGhpbSBpbiB0aGUgc29uZzs=', 'SGUsIHRvbywgaGFzIHJlc2lnbmVkIGhpcyBwYXJ0', 'SW4gdGhlIGNhc3VhbCBjb21lZHk7', 'SGUsIHRvbywgaGFzIGJlZW4gY2hhbmdlZCBpbiBoaXMgdHVybiw=', 'VHJhbnNmb3JtZWQgdXR0ZXJseTo=', 'QSB0ZXJyaWJsZSBiZWF1dHkgaXMgYm9ybi4=']

freqs = {
      'A': 0.0651738,
      'B': 0.0124248,
      'C': 0.0217339,
      'D': 0.0349835,
      'E': 0.1241442,
      'F': 0.0197881,
      'G': 0.0158610,
      'H': 0.0492888,
      'I': 0.0558094,
      'J': 0.0009033,
      'K': 0.0050529,
      'L': 0.0331490,
      'M': 0.0202124,
      'N': 0.0564513,
      'O': 0.0596302,
      'P': 0.0137645,
      'Q': 0.0008606,
      'R': 0.0497563,
      'S': 0.0515760,
      'T': 0.0729357,
      'U': 0.0225134,
      'V': 0.0082903,
      'W': 0.0171272,
      'X': 0.0013692,
      'Y': 0.0145984,
      'Z': 0.0007836,
      ' ': 0.1918182
}

def score(s):
	score = 0
	for c in s:
		cur = chr(c).upper()
		if cur in freqs:
			score += freqs[cur]
	return score

def singleByteXor(string, n):
	return b"".join( bytes.fromhex(hex(n^c)[2:].rjust(2,"0")) for c in string)

def xorStrings(data0, data1):
	ret = b"".join(bytes.fromhex(hex(data0[i]^data1[i])[2:].rjust(2,'0')) for i in range(min(len(data0),len(data1))))
	return ret

def encrypt(plaintext, key, nonce):
	aes = AES.new(key, AES.MODE_ECB)
	counter = 0
	keyStream = b""
	keyLen = (len(plaintext)//BLOCKSIZE) + 1 if len(plaintext)%16 != 0 else (len(plantext)//BLOCKSIZE)
	for i in range(0, keyLen):
		curCounter = bytes.fromhex(hex(counter)[2:(BLOCKSIZE+2)].rjust(BLOCKSIZE,'0'))[::-1]
		keyStream += aes.encrypt(nonce + curCounter)
		counter += 1
	return xorStrings(plaintext, keyStream)

def decrypt(ciphertext, key, nonce):
	aes = AES.new(key, AES.MODE_ECB)
	counter = 0
	keyStream = b""
	keyLen = (len(ciphertext)//BLOCKSIZE) + 1 if len(ciphertext)%16 != 0 else (len(ciphertext)//BLOCKSIZE)
	for i in range(0, keyLen):
		curCounter = bytes.fromhex(hex(counter)[2:(BLOCKSIZE+2)].rjust(BLOCKSIZE,'0'))[::-1]
		keyStream += aes.encrypt(nonce + curCounter)
		counter += 1
	return xorStrings(ciphertext, keyStream)

def getBlocks(ciphertexts, keySize):
	ret = []
	for i in range(keySize):
		block = b""
		for j in range(len(ciphertexts)):
			if i < len(ciphertexts[j]):
				block += bytes([ciphertexts[j][i]])
			else:
				continue
		ret.append(block)
	return ret

def getKey(blocks):
	key = b""
	for block in blocks:
		maxScore = 0
		keyGuess = b""
		for i in range(0,256):
			curTry = singleByteXor(block,i)
			curScore = score(curTry)
			if curScore > maxScore:
				maxScore = curScore
				keyGuess = bytes([i])
		key += keyGuess
	return key

def main():
	key = urandom(BLOCKSIZE)
	nonce = b'\x00'*(BLOCKSIZE//2)
	ciphertexts = []
	for pt in plaintexts:
		ciphertexts.append(decrypt(base64.b64decode(pt),key,nonce))
	keySize = len(max(ciphertexts, key=len))
	blocks = getBlocks(ciphertexts, keySize)
	keyGuess = getKey(blocks)
	for i in range(len(ciphertexts)):
		print (xorStrings(ciphertexts[i],keyGuess))

if __name__ == "__main__":
	main()

Challenge 20:

from Crypto.Cipher import AES
from os import urandom

import base64

BLOCKSIZE = 16

plaintexts = ['SSdtIHJhdGVkICJSIi4uLnRoaXMgaXMgYSB3YXJuaW5nLCB5YSBiZXR0ZXIgdm9pZCAvIFBvZXRzIGFyZSBwYXJhbm9pZCwgREoncyBELXN0cm95ZWQ=', 'Q3V6IEkgY2FtZSBiYWNrIHRvIGF0dGFjayBvdGhlcnMgaW4gc3BpdGUtIC8gU3RyaWtlIGxpa2UgbGlnaHRuaW4nLCBJdCdzIHF1aXRlIGZyaWdodGVuaW4nIQ==', 'QnV0IGRvbid0IGJlIGFmcmFpZCBpbiB0aGUgZGFyaywgaW4gYSBwYXJrIC8gTm90IGEgc2NyZWFtIG9yIGEgY3J5LCBvciBhIGJhcmssIG1vcmUgbGlrZSBhIHNwYXJrOw==', 'WWEgdHJlbWJsZSBsaWtlIGEgYWxjb2hvbGljLCBtdXNjbGVzIHRpZ2h0ZW4gdXAgLyBXaGF0J3MgdGhhdCwgbGlnaHRlbiB1cCEgWW91IHNlZSBhIHNpZ2h0IGJ1dA==', 'U3VkZGVubHkgeW91IGZlZWwgbGlrZSB5b3VyIGluIGEgaG9ycm9yIGZsaWNrIC8gWW91IGdyYWIgeW91ciBoZWFydCB0aGVuIHdpc2ggZm9yIHRvbW9ycm93IHF1aWNrIQ==', 'TXVzaWMncyB0aGUgY2x1ZSwgd2hlbiBJIGNvbWUgeW91ciB3YXJuZWQgLyBBcG9jYWx5cHNlIE5vdywgd2hlbiBJJ20gZG9uZSwgeWEgZ29uZSE=', 'SGF2ZW4ndCB5b3UgZXZlciBoZWFyZCBvZiBhIE1DLW11cmRlcmVyPyAvIFRoaXMgaXMgdGhlIGRlYXRoIHBlbmFsdHksYW5kIEknbSBzZXJ2aW4nIGE=', 'RGVhdGggd2lzaCwgc28gY29tZSBvbiwgc3RlcCB0byB0aGlzIC8gSHlzdGVyaWNhbCBpZGVhIGZvciBhIGx5cmljYWwgcHJvZmVzc2lvbmlzdCE=', 'RnJpZGF5IHRoZSB0aGlydGVlbnRoLCB3YWxraW5nIGRvd24gRWxtIFN0cmVldCAvIFlvdSBjb21lIGluIG15IHJlYWxtIHlhIGdldCBiZWF0IQ==', 'VGhpcyBpcyBvZmYgbGltaXRzLCBzbyB5b3VyIHZpc2lvbnMgYXJlIGJsdXJyeSAvIEFsbCB5YSBzZWUgaXMgdGhlIG1ldGVycyBhdCBhIHZvbHVtZQ==', 'VGVycm9yIGluIHRoZSBzdHlsZXMsIG5ldmVyIGVycm9yLWZpbGVzIC8gSW5kZWVkIEknbSBrbm93bi15b3VyIGV4aWxlZCE=', 'Rm9yIHRob3NlIHRoYXQgb3Bwb3NlIHRvIGJlIGxldmVsIG9yIG5leHQgdG8gdGhpcyAvIEkgYWluJ3QgYSBkZXZpbCBhbmQgdGhpcyBhaW4ndCB0aGUgRXhvcmNpc3Qh', 'V29yc2UgdGhhbiBhIG5pZ2h0bWFyZSwgeW91IGRvbid0IGhhdmUgdG8gc2xlZXAgYSB3aW5rIC8gVGhlIHBhaW4ncyBhIG1pZ3JhaW5lIGV2ZXJ5IHRpbWUgeWEgdGhpbms=', 'Rmxhc2hiYWNrcyBpbnRlcmZlcmUsIHlhIHN0YXJ0IHRvIGhlYXI6IC8gVGhlIFItQS1LLUktTSBpbiB5b3VyIGVhcjs=', 'VGhlbiB0aGUgYmVhdCBpcyBoeXN0ZXJpY2FsIC8gVGhhdCBtYWtlcyBFcmljIGdvIGdldCBhIGF4IGFuZCBjaG9wcyB0aGUgd2Fjaw==', 'U29vbiB0aGUgbHlyaWNhbCBmb3JtYXQgaXMgc3VwZXJpb3IgLyBGYWNlcyBvZiBkZWF0aCByZW1haW4=', 'TUMncyBkZWNheWluZywgY3V6IHRoZXkgbmV2ZXIgc3RheWVkIC8gVGhlIHNjZW5lIG9mIGEgY3JpbWUgZXZlcnkgbmlnaHQgYXQgdGhlIHNob3c=', 'VGhlIGZpZW5kIG9mIGEgcmh5bWUgb24gdGhlIG1pYyB0aGF0IHlvdSBrbm93IC8gSXQncyBvbmx5IG9uZSBjYXBhYmxlLCBicmVha3MtdGhlIHVuYnJlYWthYmxl', 'TWVsb2RpZXMtdW5tYWthYmxlLCBwYXR0ZXJuLXVuZXNjYXBhYmxlIC8gQSBob3JuIGlmIHdhbnQgdGhlIHN0eWxlIEkgcG9zc2Vz', 'SSBibGVzcyB0aGUgY2hpbGQsIHRoZSBlYXJ0aCwgdGhlIGdvZHMgYW5kIGJvbWIgdGhlIHJlc3QgLyBGb3IgdGhvc2UgdGhhdCBlbnZ5IGEgTUMgaXQgY2FuIGJl', 'SGF6YXJkb3VzIHRvIHlvdXIgaGVhbHRoIHNvIGJlIGZyaWVuZGx5IC8gQSBtYXR0ZXIgb2YgbGlmZSBhbmQgZGVhdGgsIGp1c3QgbGlrZSBhIGV0Y2gtYS1za2V0Y2g=', 'U2hha2UgJ3RpbGwgeW91ciBjbGVhciwgbWFrZSBpdCBkaXNhcHBlYXIsIG1ha2UgdGhlIG5leHQgLyBBZnRlciB0aGUgY2VyZW1vbnksIGxldCB0aGUgcmh5bWUgcmVzdCBpbiBwZWFjZQ==', 'SWYgbm90LCBteSBzb3VsJ2xsIHJlbGVhc2UhIC8gVGhlIHNjZW5lIGlzIHJlY3JlYXRlZCwgcmVpbmNhcm5hdGVkLCB1cGRhdGVkLCBJJ20gZ2xhZCB5b3UgbWFkZSBpdA==', 'Q3V6IHlvdXIgYWJvdXQgdG8gc2VlIGEgZGlzYXN0cm91cyBzaWdodCAvIEEgcGVyZm9ybWFuY2UgbmV2ZXIgYWdhaW4gcGVyZm9ybWVkIG9uIGEgbWljOg==', 'THlyaWNzIG9mIGZ1cnkhIEEgZmVhcmlmaWVkIGZyZWVzdHlsZSEgLyBUaGUgIlIiIGlzIGluIHRoZSBob3VzZS10b28gbXVjaCB0ZW5zaW9uIQ==', 'TWFrZSBzdXJlIHRoZSBzeXN0ZW0ncyBsb3VkIHdoZW4gSSBtZW50aW9uIC8gUGhyYXNlcyB0aGF0J3MgZmVhcnNvbWU=', 'WW91IHdhbnQgdG8gaGVhciBzb21lIHNvdW5kcyB0aGF0IG5vdCBvbmx5IHBvdW5kcyBidXQgcGxlYXNlIHlvdXIgZWFyZHJ1bXM7IC8gSSBzaXQgYmFjayBhbmQgb2JzZXJ2ZSB0aGUgd2hvbGUgc2NlbmVyeQ==', 'VGhlbiBub25jaGFsYW50bHkgdGVsbCB5b3Ugd2hhdCBpdCBtZWFuIHRvIG1lIC8gU3RyaWN0bHkgYnVzaW5lc3MgSSdtIHF1aWNrbHkgaW4gdGhpcyBtb29k', 'QW5kIEkgZG9uJ3QgY2FyZSBpZiB0aGUgd2hvbGUgY3Jvd2QncyBhIHdpdG5lc3MhIC8gSSdtIGEgdGVhciB5b3UgYXBhcnQgYnV0IEknbSBhIHNwYXJlIHlvdSBhIGhlYXJ0', 'UHJvZ3JhbSBpbnRvIHRoZSBzcGVlZCBvZiB0aGUgcmh5bWUsIHByZXBhcmUgdG8gc3RhcnQgLyBSaHl0aG0ncyBvdXQgb2YgdGhlIHJhZGl1cywgaW5zYW5lIGFzIHRoZSBjcmF6aWVzdA==', 'TXVzaWNhbCBtYWRuZXNzIE1DIGV2ZXIgbWFkZSwgc2VlIGl0J3MgLyBOb3cgYW4gZW1lcmdlbmN5LCBvcGVuLWhlYXJ0IHN1cmdlcnk=', 'T3BlbiB5b3VyIG1pbmQsIHlvdSB3aWxsIGZpbmQgZXZlcnkgd29yZCdsbCBiZSAvIEZ1cmllciB0aGFuIGV2ZXIsIEkgcmVtYWluIHRoZSBmdXJ0dXJl', 'QmF0dGxlJ3MgdGVtcHRpbmcuLi53aGF0ZXZlciBzdWl0cyB5YSEgLyBGb3Igd29yZHMgdGhlIHNlbnRlbmNlLCB0aGVyZSdzIG5vIHJlc2VtYmxhbmNl', 'WW91IHRoaW5rIHlvdSdyZSBydWZmZXIsIHRoZW4gc3VmZmVyIHRoZSBjb25zZXF1ZW5jZXMhIC8gSSdtIG5ldmVyIGR5aW5nLXRlcnJpZnlpbmcgcmVzdWx0cw==', 'SSB3YWtlIHlhIHdpdGggaHVuZHJlZHMgb2YgdGhvdXNhbmRzIG9mIHZvbHRzIC8gTWljLXRvLW1vdXRoIHJlc3VzY2l0YXRpb24sIHJoeXRobSB3aXRoIHJhZGlhdGlvbg==', 'Tm92b2NhaW4gZWFzZSB0aGUgcGFpbiBpdCBtaWdodCBzYXZlIGhpbSAvIElmIG5vdCwgRXJpYyBCLidzIHRoZSBqdWRnZSwgdGhlIGNyb3dkJ3MgdGhlIGp1cnk=', 'WW8gUmFraW0sIHdoYXQncyB1cD8gLyBZbywgSSdtIGRvaW5nIHRoZSBrbm93bGVkZ2UsIEUuLCBtYW4gSSdtIHRyeWluZyB0byBnZXQgcGFpZCBpbiBmdWxs', 'V2VsbCwgY2hlY2sgdGhpcyBvdXQsIHNpbmNlIE5vcmJ5IFdhbHRlcnMgaXMgb3VyIGFnZW5jeSwgcmlnaHQ/IC8gVHJ1ZQ==', 'S2FyYSBMZXdpcyBpcyBvdXIgYWdlbnQsIHdvcmQgdXAgLyBaYWtpYSBhbmQgNHRoIGFuZCBCcm9hZHdheSBpcyBvdXIgcmVjb3JkIGNvbXBhbnksIGluZGVlZA==', 'T2theSwgc28gd2hvIHdlIHJvbGxpbicgd2l0aCB0aGVuPyBXZSByb2xsaW4nIHdpdGggUnVzaCAvIE9mIFJ1c2h0b3duIE1hbmFnZW1lbnQ=', 'Q2hlY2sgdGhpcyBvdXQsIHNpbmNlIHdlIHRhbGtpbmcgb3ZlciAvIFRoaXMgZGVmIGJlYXQgcmlnaHQgaGVyZSB0aGF0IEkgcHV0IHRvZ2V0aGVy', 'SSB3YW5uYSBoZWFyIHNvbWUgb2YgdGhlbSBkZWYgcmh5bWVzLCB5b3Uga25vdyB3aGF0IEknbSBzYXlpbic/IC8gQW5kIHRvZ2V0aGVyLCB3ZSBjYW4gZ2V0IHBhaWQgaW4gZnVsbA==', 'VGhpbmtpbicgb2YgYSBtYXN0ZXIgcGxhbiAvICdDdXogYWluJ3QgbnV0aGluJyBidXQgc3dlYXQgaW5zaWRlIG15IGhhbmQ=', 'U28gSSBkaWcgaW50byBteSBwb2NrZXQsIGFsbCBteSBtb25leSBpcyBzcGVudCAvIFNvIEkgZGlnIGRlZXBlciBidXQgc3RpbGwgY29taW4nIHVwIHdpdGggbGludA==', 'U28gSSBzdGFydCBteSBtaXNzaW9uLCBsZWF2ZSBteSByZXNpZGVuY2UgLyBUaGlua2luJyBob3cgY291bGQgSSBnZXQgc29tZSBkZWFkIHByZXNpZGVudHM=', 'SSBuZWVkIG1vbmV5LCBJIHVzZWQgdG8gYmUgYSBzdGljay11cCBraWQgLyBTbyBJIHRoaW5rIG9mIGFsbCB0aGUgZGV2aW91cyB0aGluZ3MgSSBkaWQ=', 'SSB1c2VkIHRvIHJvbGwgdXAsIHRoaXMgaXMgYSBob2xkIHVwLCBhaW4ndCBudXRoaW4nIGZ1bm55IC8gU3RvcCBzbWlsaW5nLCBiZSBzdGlsbCwgZG9uJ3QgbnV0aGluJyBtb3ZlIGJ1dCB0aGUgbW9uZXk=', 'QnV0IG5vdyBJIGxlYXJuZWQgdG8gZWFybiAnY3V6IEknbSByaWdodGVvdXMgLyBJIGZlZWwgZ3JlYXQsIHNvIG1heWJlIEkgbWlnaHQganVzdA==', 'U2VhcmNoIGZvciBhIG5pbmUgdG8gZml2ZSwgaWYgSSBzdHJpdmUgLyBUaGVuIG1heWJlIEknbGwgc3RheSBhbGl2ZQ==', 'U28gSSB3YWxrIHVwIHRoZSBzdHJlZXQgd2hpc3RsaW4nIHRoaXMgLyBGZWVsaW4nIG91dCBvZiBwbGFjZSAnY3V6LCBtYW4sIGRvIEkgbWlzcw==', 'QSBwZW4gYW5kIGEgcGFwZXIsIGEgc3RlcmVvLCBhIHRhcGUgb2YgLyBNZSBhbmQgRXJpYyBCLCBhbmQgYSBuaWNlIGJpZyBwbGF0ZSBvZg==', 'RmlzaCwgd2hpY2ggaXMgbXkgZmF2b3JpdGUgZGlzaCAvIEJ1dCB3aXRob3V0IG5vIG1vbmV5IGl0J3Mgc3RpbGwgYSB3aXNo', 'J0N1eiBJIGRvbid0IGxpa2UgdG8gZHJlYW0gYWJvdXQgZ2V0dGluJyBwYWlkIC8gU28gSSBkaWcgaW50byB0aGUgYm9va3Mgb2YgdGhlIHJoeW1lcyB0aGF0IEkgbWFkZQ==', 'U28gbm93IHRvIHRlc3QgdG8gc2VlIGlmIEkgZ290IHB1bGwgLyBIaXQgdGhlIHN0dWRpbywgJ2N1eiBJJ20gcGFpZCBpbiBmdWxs', 'UmFraW0sIGNoZWNrIHRoaXMgb3V0LCB5byAvIFlvdSBnbyB0byB5b3VyIGdpcmwgaG91c2UgYW5kIEknbGwgZ28gdG8gbWluZQ==', 'J0NhdXNlIG15IGdpcmwgaXMgZGVmaW5pdGVseSBtYWQgLyAnQ2F1c2UgaXQgdG9vayB1cyB0b28gbG9uZyB0byBkbyB0aGlzIGFsYnVt', 'WW8sIEkgaGVhciB3aGF0IHlvdSdyZSBzYXlpbmcgLyBTbyBsZXQncyBqdXN0IHB1bXAgdGhlIG11c2ljIHVw', 'QW5kIGNvdW50IG91ciBtb25leSAvIFlvLCB3ZWxsIGNoZWNrIHRoaXMgb3V0LCB5byBFbGk=', 'VHVybiBkb3duIHRoZSBiYXNzIGRvd24gLyBBbmQgbGV0IHRoZSBiZWF0IGp1c3Qga2VlcCBvbiByb2NraW4n', 'QW5kIHdlIG91dHRhIGhlcmUgLyBZbywgd2hhdCBoYXBwZW5lZCB0byBwZWFjZT8gLyBQZWFjZQ==']

freqs = {
      'A': 0.0651738,
      'B': 0.0124248,
      'C': 0.0217339,
      'D': 0.0349835,
      'E': 0.1241442,
      'F': 0.0197881,
      'G': 0.0158610,
      'H': 0.0492888,
      'I': 0.0558094,
      'J': 0.0009033,
      'K': 0.0050529,
      'L': 0.0331490,
      'M': 0.0202124,
      'N': 0.0564513,
      'O': 0.0596302,
      'P': 0.0137645,
      'Q': 0.0008606,
      'R': 0.0497563,
      'S': 0.0515760,
      'T': 0.0729357,
      'U': 0.0225134,
      'V': 0.0082903,
      'W': 0.0171272,
      'X': 0.0013692,
      'Y': 0.0145984,
      'Z': 0.0007836,
      ' ': 0.1918182
}

def score(s):
	score = 0
	for c in s:
		cur = chr(c).upper()
		if cur in freqs:
			score += freqs[cur]
	return score

def singleByteXor(string, n):
	return b"".join( bytes.fromhex(hex(n^c)[2:].rjust(2,"0")) for c in string)

def xorStrings(data0, data1):
	ret = b"".join(bytes.fromhex(hex(data0[i]^data1[i])[2:].rjust(2,'0')) for i in range(min(len(data0),len(data1))))
	return ret

def encrypt(plaintext, key, nonce):
	aes = AES.new(key, AES.MODE_ECB)
	counter = 0
	keyStream = b""
	keyLen = (len(plaintext)//BLOCKSIZE) + 1 if len(plaintext)%16 != 0 else (len(plantext)//BLOCKSIZE)
	for i in range(0, keyLen):
		curCounter = bytes.fromhex(hex(counter)[2:(BLOCKSIZE+2)].rjust(BLOCKSIZE,'0'))[::-1]
		keyStream += aes.encrypt(nonce + curCounter)
		counter += 1
	return xorStrings(plaintext, keyStream)

def decrypt(ciphertext, key, nonce):
	aes = AES.new(key, AES.MODE_ECB)
	counter = 0
	keyStream = b""
	keyLen = (len(ciphertext)//BLOCKSIZE) + 1 if len(ciphertext)%16 != 0 else (len(ciphertext)//BLOCKSIZE)
	for i in range(0, keyLen):
		curCounter = bytes.fromhex(hex(counter)[2:(BLOCKSIZE+2)].rjust(BLOCKSIZE,'0'))[::-1]
		keyStream += aes.encrypt(nonce + curCounter)
		counter += 1
	return xorStrings(ciphertext, keyStream)

def getBlocks(ciphertexts, keySize):
	ret = []
	for i in range(keySize):
		block = b""
		for j in range(len(ciphertexts)):
			if i < len(ciphertexts[j]):
				block += bytes([ciphertexts[j][i]])
			else:
				continue
		ret.append(block)
	return ret

def getKey(blocks):
	key = b""
	for block in blocks:
		maxScore = 0
		keyGuess = b""
		for i in range(0,256):
			curTry = singleByteXor(block,i)
			curScore = score(curTry)
			if curScore > maxScore:
				maxScore = curScore
				keyGuess = bytes([i])
		key += keyGuess
	return key

def main():
	key = urandom(BLOCKSIZE)
	nonce = b'\x00'*(BLOCKSIZE//2)
	ciphertexts = []
	for pt in plaintexts:
		ciphertexts.append(decrypt(base64.b64decode(pt),key,nonce))
	keySize = len(max(ciphertexts, key=len))
	blocks = getBlocks(ciphertexts, keySize)
	keyGuess = getKey(blocks)
	for i in range(len(ciphertexts)):
		print (xorStrings(ciphertexts[i],keyGuess))

if __name__ == "__main__":
	main()

Challenge 21:

def getLowestBits(n, number_of_bits):
    mask = (1 << number_of_bits) - 1
    return n & mask

class MT19937:

	# Coefficients for MT19937-32
	w, n, m, r = 32, 624, 397, 31
	a = 0x9908b0df
	u, d = 11, 0xffffffff
	s, b = 7, 0x9d2c5680
	t, c = 15, 0xefc60000
	l = 18
	f = 1812433253
	lowerMask = (1 << r) - 1
	upperMask = getLowestBits(not lowerMask, w)

	def __init__(self,seed):
		self.MT = []

		self.index = self.n
		self.MT.append(seed)
		for i in range(1, self.index):
			self.MT.append(getLowestBits(self.f * (self.MT[i - 1] ^ (self.MT[i - 1] >> (self.w - 2))) + i, self.w))


	def extract_number(self):
		if self.index >= self.n:
			self.twist()

		y = self.MT[self.index]
		y ^= (y >> self.u) & self.d
		y ^= (y << self.s) & self.b
		y ^= (y << self.t) & self.c
		y ^= (y >> self.l)

		self.index += 1
		return getLowestBits(y, self.w)

	def twist(self):
		for i in range(self.n):
			x = (self.MT[i] & self.upperMask) + (self.MT[(i + 1) % self.n] & self.lowerMask)
			x_a = x >> 1
			if x % 2 != 0:
 				x_a ^= self.a

			self.MT[i] = self.MT[(i + self.m) % self.n] ^ x_a

		self.index = 0


def main():
    # Check if the numbers look random
    print(MT19937(0).extract_number())


if __name__ == '__main__':
    main()

Challenge 22:

import random
import time


def getLowestBits(n, number_of_bits):
    mask = (1 << number_of_bits) - 1
    return n & mask

class MT19937:

	# Coefficients for MT19937-32
	w, n, m, r = 32, 624, 397, 31
	a = 0x9908b0df
	u, d = 11, 0xffffffff
	s, b = 7, 0x9d2c5680
	t, c = 15, 0xefc60000
	l = 18
	f = 1812433253
	lowerMask = (1 << r) - 1
	upperMask = getLowestBits(not lowerMask, w)

	def __init__(self,seed):
		self.MT = []

		self.index = self.n
		self.MT.append(seed)
		for i in range(1, self.index):
			self.MT.append(getLowestBits(self.f * (self.MT[i - 1] ^ (self.MT[i - 1] >> (self.w - 2))) + i, self.w))


	def extract_number(self):
		if self.index >= self.n:
			self.twist()

		y = self.MT[self.index]
		y ^= (y >> self.u) & self.d
		y ^= (y << self.s) & self.b
		y ^= (y << self.t) & self.c
		y ^= (y >> self.l)

		self.index += 1
		return getLowestBits(y, self.w)

	def twist(self):
		for i in range(self.n):
			x = (self.MT[i] & self.upperMask) + (self.MT[(i + 1) % self.n] & self.lowerMask)
			x_a = x >> 1
			if x % 2 != 0:
 				x_a ^= self.a

			self.MT[i] = self.MT[(i + self.m) % self.n] ^ x_a

		self.index = 0

#########################################################################################################

def runRoutine():
	time.sleep(random.randint(40,100))
	seed = int(time.time())
	mt = MT19937(seed)
	time.sleep(random.randint(40,100))
	return seed, mt.extract_number()


def crackSeed(target):
	curSeed = int(time.time())
	curTry = MT19937(curSeed)
	while curTry.extract_number() != target:
		curSeed -= 1
		curTry = MT19937(curSeed)
	return curSeed

def main():
	print ("Running routine...")
	originalSeed, target = runRoutine()
	print ("Original seed:",originalSeed,"\nTarget output:",target,"\n")
	seed = crackSeed(target)
	print ("Found original seed:",seed)

if __name__ == '__main__':
    main()

Challenge 23:

from os import urandom

def getLowestBits(n, number_of_bits):
    mask = (1 << number_of_bits) - 1
    return n & mask

class MT19937:

	# Coefficients for MT19937-32
	w, n, m, r = 32, 624, 397, 31
	a = 0x9908b0df
	u, d = 11, 0xffffffff
	s, b = 7, 0x9d2c5680
	t, c = 15, 0xefc60000
	l = 18
	f = 1812433253
	lowerMask = (1 << r) - 1
	upperMask = getLowestBits(not lowerMask, w)

	def __init__(self,seed):
		self.MT = []

		self.index = self.n
		self.MT.append(seed)
		for i in range(1, self.index):
			self.MT.append(getLowestBits(self.f * (self.MT[i - 1] ^ (self.MT[i - 1] >> (self.w - 2))) + i, self.w))


	def extractNumber(self):
		if self.index >= self.n:
			self.twist()
		y = self.MT[self.index]
		y ^= (y >> self.u) & self.d
		y ^= (y << self.s) & self.b
		y ^= (y << self.t) & self.c
		y ^= (y >> self.l)
		self.index += 1
		return getLowestBits(y, self.w)

	def twist(self):
		for i in range(self.n):
			x = (self.MT[i] & self.upperMask) + (self.MT[(i + 1) % self.n] & self.lowerMask)
			x_a = x >> 1
			if x % 2 != 0:
 				x_a ^= self.a

			self.MT[i] = self.MT[(i + self.m) % self.n] ^ x_a

		self.index = 0


def untemper(x):
	x ^= (x >> MT19937.l)
	x ^= (x << MT19937.t) & MT19937.c

	smask = (1 << MT19937.s) - 1
	x ^= (x << MT19937.s) & MT19937.b & (smask << MT19937.s)
	x ^= (x << MT19937.s) & MT19937.b & (smask << (MT19937.s*2))
	x ^= (x << MT19937.s) & MT19937.b & (smask << (MT19937.s*3))
	x ^= (x << MT19937.s) & MT19937.b & (smask << (MT19937.s*4))

	umask = (1 << MT19937.u) - 1
	x ^= (x >> MT19937.u) & (umask << (MT19937.u*2))
	x ^= (x >> MT19937.u) & (umask << MT19937.u)
	x ^= (x >> MT19937.u) & umask

	return x


def main():
	seed = int(bytes.hex(urandom(10)),16)
	mt = MT19937(seed)
	newMT = []
	for i in range(MT19937.n):
		newMT.append( untemper(mt.extractNumber()) )
	clonedMT = MT19937(0)
	clonedMT.MT = newMT

	for i in range(1337):
		curOriginal =  mt.extractNumber()
		curCloned = clonedMT.extractNumber()
		print ("\nOriginal Generated:",curOriginal)
		print ("Cloned Generated:",curCloned)
		if curOriginal != curCloned:
			print ("Error: different values generated!")
			break



if __name__ == '__main__':
    main()

Challenge 24:

from os import urandom

def xor(s1, s2):
	assert len(s1) == len(s2)
	ret = b"".join( bytes.fromhex(hex(s1[i]^s2[i])[2:].rjust(2,"0")) for i in range(0,len(s1)) )
	return ret

def getLowestBits(n, number_of_bits):
    mask = (1 << number_of_bits) - 1
    return n & mask

class MT19937:

	# Coefficients for MT19937-32
	w, n, m, r = 32, 624, 397, 31
	a = 0x9908b0df
	u, d = 11, 0xffffffff
	s, b = 7, 0x9d2c5680
	t, c = 15, 0xefc60000
	l = 18
	f = 1812433253
	lowerMask = (1 << r) - 1
	upperMask = getLowestBits(not lowerMask, w)

	def __init__(self,seed):
		self.MT = []

		self.index = self.n
		self.MT.append(seed)
		for i in range(1, self.index):
			self.MT.append(getLowestBits(self.f * (self.MT[i - 1] ^ (self.MT[i - 1] >> (self.w - 2))) + i, self.w))


	def extractNumber(self):
		if self.index >= self.n:
			self.twist()

		y = self.MT[self.index]
		y ^= (y >> self.u) & self.d
		y ^= (y << self.s) & self.b
		y ^= (y << self.t) & self.c
		y ^= (y >> self.l)

		self.index += 1
		return getLowestBits(y, self.w)

	def twist(self):
		for i in range(self.n):
			x = (self.MT[i] & self.upperMask) + (self.MT[(i + 1) % self.n] & self.lowerMask)
			x_a = x >> 1
			if x % 2 != 0:
 				x_a ^= self.a

			self.MT[i] = self.MT[(i + self.m) % self.n] ^ x_a

		self.index = 0


class MT19937Cipher:

	def __init__(self,seed):
		self.mt = MT19937(seed)

	def encrypt(self,plaintext):
		keyStream = b""
		while len(keyStream) < len(plaintext):
			keyStream += str(self.mt.extractNumber()).encode()
		#ciphertext = hex(int(bytes.hex(plaintext),16) ^ int(bytes.hex(keyStream[:len(plaintext)]),16))[2:].encode()
		return xor(plaintext,keyStream[:len(plaintext)])

def breakCipher(ciphertext,crib):
	for i in range(0,2**16):
		curMT = MT19937Cipher(i).encrypt(ciphertext)
		if crib in curMT:
			return i
	return -1

def main():
	seed = int(bytes.hex(urandom(2)),16)
	prefix = urandom(int(bytes.hex(urandom(1)),16))
	plaintext = prefix + b"A"*16
	ciphertext = MT19937Cipher(seed).encrypt(plaintext)
	recoveredSeed = breakCipher(ciphertext, b"A"*16)
	if recoveredSeed != -1:
		print ("Seed recovered successfully.\nOriginal plaintext:", MT19937Cipher(recoveredSeed).encrypt(ciphertext))
	else:
		print ("Failed to recover seed")

if __name__ == '__main__':
    main()