Implementing a Feistel cipher with Python
Background
During my Winter 2020 term at Portland State University, I completed the CS485: Cryptography elective with Dr. Sarah Mocas. During this course, I had a chance to gain hands-on expirience implementing two cryptographic algorithms: a Feistel cipher and the ElGamal encryption algorithm. In this post, I would like to share the details of my implementation of a Feistel cipher using a 64 bit block size and a 64 bit key using Python 3.
Implementation
I chose Python because of its native support of large numbers, as well as very easy conversion between decimal and hexidecimal numbers. Officiency of the implementation was not an issue due to it being only an educational project to help students gain better understanding of cryptography.
The cipher consist of 3 major steps:
Step 1: Key generation
The key generation algorithm works as follows:
- Uses the 64 bit secret key K (8 bytes)
- Left rotates K by 1 bit 192 times (64 * 3)
- Creates 16 new keys of consisting of 12 bytes
1 | def shiftBitLeft(self, block): |
Step 2: Encryption
- Input whitening step:
- The input is the 64-bit block divided into 4 words w0, w1, w2, w3.
- XOR each word with 16 bits of the key K = K0K1K2K3.
- The output is
- R0 = w0 ⊕ K0, R01 = w1 ⊕ K1,
- R2 = w2 ⊕ K2, R3 = w3 ⊕ K3.
- Set the round number to round=0. After each of the 16 rounds increment round by one.
- Compute F(R0, R1, round). This function returns two values F0 and F1.
- Compute R2 ⊕ F0. This value is R0 for the next round.
- Compute R3 ⊕ F1. This value is R1 for the next round.
- The value R0 becomes R2 for the next round.
- The value R1 becomes R3 for the next round.
- Repeat for 16 rounds (but not the whitening step this only happens at the start of the first round).
- After the last of the 16 rounds:
- Undo the last swap by setting
y0 to R2 from the end of the 16th round;
y1 to R3 from the end of the 16th round;
y2 to R0 from the end of the 16th round and
y3 to R1 from the end of the 16th round.
- Undo the last swap by setting
- Output whitening step:
- XOR each word with 16 bits of the key K = K0K1K2K3.
The input is y0y1y2y3. The output is - C0 = y0 ⊕ K0, C01 = y1 ⊕ K1,
- C2 = y2 ⊕ K2, C3 = y3 ⊕ K3.
The value C0C1C2C3 is the ciphertext.
- XOR each word with 16 bits of the key K = K0K1K2K3.
1 | def whitening(self, word, key): |
Step 3: Decryption
The algorithm to decrypt is the same as the algorithm to encrypt except that the keys are generated and used in reverse order.
1 | def decrypt(self, ct_hex): |
Further Reading
You can find the full code of my implementation here.