# Can HKDF be safely used as a round function in a Feistel Cipher

Hello my question is it practical to use HKD as a round function in a Feistel Cipher I know you should not roll your own crypto, this is for learning purpose's only.

Take a look at my code and let me know what you think I'm not sure if I implemented the Cipher correctly this is my second attempt at cryptography my first go was a simple onetime pad in python.

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.backends import default_backend
from os import urandom

class FeistelCipher:
def __init__(self, key, rounds=128):
self.key = key
self.rounds = rounds
self.backend = default_backend()

def round_function(self, data, round_key):
# Use HKDF as a pseudo-random function for the round function
hkdf = HKDF(
algorithm=hashes.SHA256(),
length=len(data),
salt=None,
info=round_key,
backend=self.backend
)
return hkdf.derive(data)

def encrypt(self, plaintext):
assert len(plaintext) % 2 == 0, "Plaintext length must be even"
L, R = plaintext[:len(plaintext)//2], plaintext[len(plaintext)//2:]

for i in range(self.rounds):
round_key = (self.key + bytes([i])).ljust(len(L), b'\x00')
F = self.round_function(R, round_key)
L, R = R, bytes(a ^ b for a, b in zip(L, F))

return L + R

def decrypt(self, ciphertext):
assert len(ciphertext) % 2 == 0, "Ciphertext length must be even"
L, R = ciphertext[:len(ciphertext)//2], ciphertext[len(ciphertext)//2:]

for i in reversed(range(self.rounds)):
round_key = (self.key + bytes([i])).ljust(len(L), b'\x00')
F = self.round_function(L, round_key)
R, L = L, bytes(a ^ b for a, b in zip(R, F))

return L + R

# Calculate the number of bytes needed to make the data a multiple of the block size
padding_needed = block_size - (len(data) % block_size)
# Create the padding bytes, each byte is the same as the number of padding bytes added
# Append the padding to the data

# Remove the padding from the data

# Usage
key = urandom(64) # Generate a random key
cipher = FeistelCipher(key)

# Encryption
plaintext = b"Padding is a way to take data that may or may not be a multiple of the block size for a cipher and extend it out so that it is. This is required for many block cipher modes as they require the data to be encrypted to be an exact multiple of the block size."  # The plaintext does not have an even length
block_size = 16  # Define block size for padding purposes

# Decryption
decrypted_padded = cipher.decrypt(encrypted)  # Decrypt the ciphertext

print(f"Original: {plaintext}")
print(f"Encrypted: {encrypted}")


All constructive criticism welcome

I didn't look too closely at the code for all issues. But in general, you could use HKDF to implement a Feistel cipher for the sake of an experiment. The way this is implemented currently uses HKDF improperly. The HKDF API might be confusing, but it is also well-documented...

Anyway, what you are doing currently is

$$\verb| k_i = HKDF(ikm=data, info=concat(k,i),salt=None...)|.$$

So, you are using data as keying material. This might be fine due to the properties of the underlying HMAC, but it's likely to void the "provable security". guarantees of the scheme.

A suggestion for improvement (and enhancing performance) is to precompute round keys upon the creation of the cipher object.

For this you can use $$\verb| k_i = HKDF-Expand(ikm=data, info=concat(i, other_context_info)..)|.$$

Your API might not give direct access to HKDF-Expand so you may use the normal HKDF API as well. Just make sure you pass the right parameters. In each round, you can implement the round function as $$\verb|HMAC(key=k_i,input=data)|$$.

Additional remarks: For info, you might want to pass in more information like the block size, for example. Your current implementation allows for various block sizes and only checks that the input to encrypt has an even length. Which allows the same key to be used with different input domains. This is not much of an issue for a side project but maybe something to keep in mind in general.

• thanks for the insight working on improving the code Commented Feb 9 at 10:44