+def unpad_pkcs7(data):
+ return data[:-compat_ord(data[-1])]
+
+
+def pkcs7_padding(data):
+ """
+ PKCS#7 padding
+
+ @param {int[]} data cleartext
+ @returns {int[]} padding data
+ """
+
+ remaining_length = BLOCK_SIZE_BYTES - len(data) % BLOCK_SIZE_BYTES
+ return data + [remaining_length] * remaining_length
+
+
+def pad_block(block, padding_mode):
+ """
+ Pad a block with the given padding mode
+ @param {int[]} block block to pad
+ @param padding_mode padding mode
+ """
+ padding_size = BLOCK_SIZE_BYTES - len(block)
+
+ PADDING_BYTE = {
+ 'pkcs7': padding_size,
+ 'iso7816': 0x0,
+ 'whitespace': 0x20,
+ 'zero': 0x0,
+ }
+
+ if padding_size < 0:
+ raise ValueError('Block size exceeded')
+ elif padding_mode not in PADDING_BYTE:
+ raise NotImplementedError(f'Padding mode {padding_mode} is not implemented')
+
+ if padding_mode == 'iso7816' and padding_size:
+ block = block + [0x80] # NB: += mutates list
+ padding_size -= 1
+
+ return block + [PADDING_BYTE[padding_mode]] * padding_size
+
+
+def aes_ecb_encrypt(data, key, iv=None):
+ """
+ Encrypt with aes in ECB mode. Using PKCS#7 padding
+
+ @param {int[]} data cleartext
+ @param {int[]} key 16/24/32-Byte cipher key
+ @param {int[]} iv Unused for this mode
+ @returns {int[]} encrypted data
+ """
+ expanded_key = key_expansion(key)
+ block_count = int(ceil(float(len(data)) / BLOCK_SIZE_BYTES))
+
+ encrypted_data = []
+ for i in range(block_count):
+ block = data[i * BLOCK_SIZE_BYTES: (i + 1) * BLOCK_SIZE_BYTES]
+ encrypted_data += aes_encrypt(pkcs7_padding(block), expanded_key)
+
+ return encrypted_data
+
+
+def aes_ecb_decrypt(data, key, iv=None):
+ """
+ Decrypt with aes in ECB mode
+
+ @param {int[]} data cleartext
+ @param {int[]} key 16/24/32-Byte cipher key
+ @param {int[]} iv Unused for this mode
+ @returns {int[]} decrypted data
+ """
+ expanded_key = key_expansion(key)
+ block_count = int(ceil(float(len(data)) / BLOCK_SIZE_BYTES))
+
+ encrypted_data = []
+ for i in range(block_count):
+ block = data[i * BLOCK_SIZE_BYTES: (i + 1) * BLOCK_SIZE_BYTES]
+ encrypted_data += aes_decrypt(block, expanded_key)
+ encrypted_data = encrypted_data[:len(data)]
+
+ return encrypted_data
+
+
+def aes_ctr_decrypt(data, key, iv):