I’m working on implementing a custom cryptographic algorithm and I’m encountering an issue where the output of my encryption and decryption processes doesn’t match the expected results. Specifically, I’m not able to correctly decrypt the ciphertext back to the original plaintext.
Block Size:
Input/Output Block Size: 128 bits (16 bytes)
Key Generation:
Initial Key: A 128-bit key is used to generate 16 round keys.
Round Key Generation: The initial key is processed through a key expansion algorithm to produce 16 round keys. Each round key is derived by applying a fixed transformation to the previous round key.
Encryption Process:
Initial State: The plaintext is divided into two halves (L0, R0) of 64 bits each.
Rounds: The encryption process consists of 16 rounds. In each round:
Round Key Application: The round key is XORed with the right half (R).
Feistel Function: A Feistel function is applied to the result, involving substitution and permutation.
Swap: The left and right halves are swapped for the next round.
Final State: After 16 rounds, the final output is obtained by concatenating the left and right halves.
I’m encountering issues where the decrypted output does not match the original plaintext. Despite following the algorithm and applying the round keys correctly, the decrypted text is incorrect or empty.
What I’ve Tried:
-Verified key generation and round key application.
-Checked the implementation of the substitution, permutation, and other operations.
-Debugged step-by-step to ensure each operation is applied correctly.
Thank you
def pad(plaintext, block_size):
padding_length = block_size - (len(plaintext) % block_size)
padding = bytes([padding_length] * padding_length)
return plaintext + padding
def unpad(padded_plaintext):
padding_length = padded_plaintext[-1]
return padded_plaintext[:-padding_length]
def key_expansion(key):
# Ensure key is 16 bytes for 128-bit key
if len(key) != 16:
raise ValueError("Key must be 16 bytes long for 128-bit key")
# Convert key into an integer for processing
K = int.from_bytes(key, byteorder='big')
round_keys = []
# Generate round keys
for i in range(17):
# Calculate round key by rotating and applying transformations
round_key = (K ^ (i * 0x1B)) % (1 << 128)
round_keys.append(round_key.to_bytes(16, byteorder='big'))
return round_keys
CP_TABLE = [0, 32, 1, 33, 2, 34, 3, 35, 4, 36, 5, 37, 6, 38, 7, 39, 8, 40, 9, 41, 10, 42, 11, 43, 12, 44, 13, 45, 14, 46, 15, 47, 16, 48, 17, 49, 18, 50, 19, 51, 20, 52, 21, 53, 22, 54, 23, 55, 24, 56, 25, 57, 26, 58, 27, 59, 28, 60, 29, 61, 30, 62, 31, 63]
FP_TABLE = [57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7, 56, 48, 40, 32, 24, 16, 8, 0, 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6]
def permute(block, table):
permuted_block = 0
for index, bit_pos in enumerate(table):
permuted_block |= ((block >> bit_pos) & 1) << index
return permuted_block
SBOX = [
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
]
INV_SBOX = [SBOX.index(x) for x in range(256)]
INV_CP_TABLE = [CP_TABLE.index(i) for i in range(64)]
INV_FP_TABLE = [FP_TABLE.index(i) for i in range(64)]
def sub_bytes_8(state):
return [SBOX[b] for b in state]
def shift_rows_8(state):
return [
state[0], state[1], state[2], state[3],
state[4], state[5], state[6], state[7]
]
def gf_mult(a, b):
p = 0
hi_bit_set = 0
for _ in range(8):
if b & 1:
p ^= a
hi_bit_set = a & 0x80
a <<= 1
if hi_bit_set:
a ^= 0x1b
b >>= 1
return p % 256
def mix_columns_8(state):
result = [0] * 8
for c in range(2):
col = state[c*4:(c+1)*4]
result[c*4] = gf_mult(2, col[0]) ^ gf_mult(3, col[1]) ^ col[2] ^ col[3]
result[c*4+1] = col[0] ^ gf_mult(2, col[1]) ^ gf_mult(3, col[2]) ^ col[3]
result[c*4+2] = col[0] ^ col[1] ^ gf_mult(2, col[2]) ^ gf_mult(3, col[3])
result[c*4+3] = gf_mult(3, col[0]) ^ col[1] ^ col[2] ^ gf_mult(2, col[3])
return result
def add_round_key(state, round_key):
round_key_bytes = list(round_key)
return [b ^ round_key_bytes[i] for i, b in enumerate(state)]
def encrypt_block_8(block, round_keys):
block = int.from_bytes(block, byteorder='big')
block = permute(block, CP_TABLE)
block = block.to_bytes(8, byteorder='big')
state = list(block)
print(f"Initial State: {state}")
state = add_round_key(state, round_keys[0])
print(f"After Add Round Key: {state}")
for i in range(1, 17):
state = sub_bytes_8(state)
state = shift_rows_8(state)
state = mix_columns_8(state)
state = add_round_key(state, round_keys[i])
print(f"After Round {i}: {state}")
block = int.from_bytes(state, byteorder='big')
block = permute(block, FP_TABLE)
return block.to_bytes(8, byteorder='big')
def encrypt_message(message, key):
block_size = 8 # 64-bit block size = 8 bytes
padded_message = pad(message.encode('utf-8'), block_size)
round_keys = key_expansion(key)
ciphertext = b''
for i in range(0, len(padded_message), block_size):
block = padded_message[i:i+block_size]
ciphertext += encrypt_block_8(block, round_keys)
return ciphertext
def inv_mix_columns_8(state):
result = [0] * 8
for c in range(2):
col = state[c*4:(c+1)*4]
result[c*4] = gf_mult(14, col[0]) ^ gf_mult(11, col[1]) ^ gf_mult(13, col[2]) ^ gf_mult(9, col[3])
result[c*4+1] = gf_mult(9, col[0]) ^ gf_mult(14, col[1]) ^ gf_mult(11, col[2]) ^ gf_mult(13, col[3])
result[c*4+2] = gf_mult(13, col[0]) ^ gf_mult(9, col[1]) ^ gf_mult(14, col[2]) ^ gf_mult(11, col[3])
result[c*4+3] = gf_mult(11, col[0]) ^ gf_mult(13, col[1]) ^ gf_mult(9, col[2]) ^ gf_mult(14, col[3])
return result
def inv_sub_bytes_8(state):
return [INV_SBOX[b] for b in state]
def inv_shift_rows_8(state):
return [
state[0], state[1], state[2], state[3],
state[4], state[5], state[6], state[7]
]
def decrypt_block_8(block, round_keys):
block = int.from_bytes(block, byteorder='big')
block = permute(block, INV_FP_TABLE)
block = block.to_bytes(8, byteorder='big')
state = list(block)
state = add_round_key(state, round_keys[16])
for i in range(15, 0, -1):
state = inv_mix_columns_8(state)
state = inv_shift_rows_8(state)
state = inv_sub_bytes_8(state)
state = add_round_key(state, round_keys[i])
state = add_round_key(state, round_keys[0])
block = int.from_bytes(state, byteorder='big')
block = permute(block, INV_CP_TABLE)
return block.to_bytes(8, byteorder='big')
def decrypt_message(ciphertext, key):
block_size = 8 # 64-bit block size = 8 bytes
round_keys = key_expansion(key)
plaintext = b''
for i in range(0, len(ciphertext), block_size):
block = ciphertext[i:i+block_size]
plaintext += decrypt_block_8(block, round_keys)
print("Plain Text: ", plaintext.hex)
return unpad(plaintext).decode('utf-8')
key = b'x01' * 16 #128-bit key
# plaintext = b'x06' * 8 #64-bit plaintext
message = "I love cybersecurity"
round_keys = key_expansion(key)
for i, rk in enumerate(round_keys):
print(f"Round Key {i}: {rk.hex()}")
# Encrypt
# ciphertext = encrypt_block_8(plaintext, round_keys)
ciphertext = encrypt_message(message, key)
print("Ciphertext:", ciphertext)
# Decrypt
# decrypted_text = decrypt_block_8(ciphertext, round_keys)
decrypted_text = decrypt_message(ciphertext, key)
print("Decrypted Text:", decrypted_text)
ALEXBEET 22 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.