Understanding Cryptography by Christof Paar and Jan Pelzl - Chapter 3 Solutions - Ex3.13
- 6 mins- Return to index
- Exercise 3.1
- Exercise 3.2
- Exercise 3.3
- Exercise 3.4
- Exercise 3.5
- Exercise 3.6
- Exercise 3.7
- Exercise 3.8
- Exercise 3.9
- Exercise 3.10
- Exercise 3.11
- Exercise 3.12
- Exercise 3.13
Exercise 3.13
This problem deals with the lightweight cipher PRESENT.
- Calculate the state of PRESENT-80 after the execution of one round. You can use the following table to solve this problem with paper and pencil. Use the following values (in hexadecimal notation):
- plaintext = 0000 0000 0000 0000,
- key = BBBB 5555 5555 EEEE FFFF.
- Now calculate the round key for the second round using the following table.
Solution
This solution is verified as correct by the official Solutions for Odd-Numbered Questions manual.
1. After round one, the state is as follows:
2. The generation of the round key for the second round is as follows (Round 1 does not modify the key register):
I wrote a python script which can perform PRESENT-80 encryption:
Note: This is not a remotely efficient implementation of PRESENT-80.
from bitarray import bitarray
def generate_round_keys(key, verbose=False):
yield key[:64]
for i in range(1, 32):
key = key[-19:] + key[:-19]
if verbose:
print "KeyState After Rotation:\t", bit_array_to_hex(key)
key = sbox(key[:4]) + key[4:]
if verbose:
print "KeyState After S-box:\t\t", bit_array_to_hex(key)
round_counter = make_bit_array(5, i)
key = key[:-20] + (key[-20:-15] ^ round_counter) + key[-15:]
if verbose:
print "KeyState After Counter Add:\t", bit_array_to_hex(key)
yield key[:64]
def sbox(n):
sbox = {
'0000': bitarray('1100'),
'0001': bitarray('0101'),
'0010': bitarray('0110'),
'0011': bitarray('1011'),
'0100': bitarray('1001'),
'0101': bitarray('0000'),
'0110': bitarray('1010'),
'0111': bitarray('1101'),
'1000': bitarray('0011'),
'1001': bitarray('1110'),
'1010': bitarray('1111'),
'1011': bitarray('1000'),
'1100': bitarray('0100'),
'1101': bitarray('0111'),
'1110': bitarray('0001'),
'1111': bitarray('0010'),
}
return sbox[n.to01()][:]
def apply_sbox(ciphertext):
return \
sbox(ciphertext[0:4]) + \
sbox(ciphertext[4:8]) + \
sbox(ciphertext[8:12]) + \
sbox(ciphertext[12:16]) + \
sbox(ciphertext[16:20]) + \
sbox(ciphertext[20:24]) + \
sbox(ciphertext[24:28]) + \
sbox(ciphertext[28:32]) + \
sbox(ciphertext[32:36]) + \
sbox(ciphertext[36:40]) + \
sbox(ciphertext[40:44]) + \
sbox(ciphertext[44:48]) + \
sbox(ciphertext[48:52]) + \
sbox(ciphertext[52:56]) + \
sbox(ciphertext[56:60]) + \
sbox(ciphertext[60:64])
def apply_pLayer(ciphertext):
temp = ciphertext[:]
temp.setall(0)
for i in range(0, 64):
if i == 63:
temp[63] = ciphertext[63]
else:
pi = (i * 16) % 63
temp[pi] = ciphertext[i]
return temp
def present_80_encrypt(plaintext, key, verbose=False):
ciphertext = make_bit_array(64, plaintext)
key = make_bit_array(80, key)
i = 0
for ki in generate_round_keys(key, verbose):
i += 1
ciphertext ^= ki
if verbose:
print "Round {i:02d} Key:\t\t\t".format(i=i), bit_array_to_hex(ki)
print "State After KeyAdd:\t\t", bit_array_to_hex(ciphertext)
if i == 32:
break
ciphertext = apply_sbox(ciphertext)
if verbose:
print "State After SBox:\t\t", bit_array_to_hex(ciphertext)
ciphertext = apply_pLayer(ciphertext)
if verbose:
print "State After pLayer:\t\t", bit_array_to_hex(ciphertext)
if verbose:
print "--------"
def make_bit_array(length, n):
return bitarray(bin(n).lstrip('-0b').zfill(length))
def bit_array_to_hex(b):
return hex(int(b.to01(), 2)).lstrip('-0x').rstrip('L-').zfill(16)
if __name__ == "__main__":
plaintext = int('0000000000000000', 16)
key = int('BBBB55555555EEEEFFFF', 16)
present_80_encrypt(plaintext, key, verbose=True)