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)