Skip to content

Instantly share code, notes, and snippets.

@HarryRybacki-zz
Created February 15, 2014 23:48
Show Gist options
  • Save HarryRybacki-zz/9027020 to your computer and use it in GitHub Desktop.
Save HarryRybacki-zz/9027020 to your computer and use it in GitHub Desktop.
"""
This code is written to serve individuals learning about Feistal networks.
Notes:
Key length: 8-bits
Subkeys: 8, 8-bit subkeys generated from original key
Round function: Defaults to the XOR (exlusive or) of a given rounds right
side input and the corresponding subkey.
Block size: Messages are broken down into 16-bit 'blocks' and then encrypted/decrypted
Fiestal Round: Each block is run through 8 rounds
The 8-bit primary key and a simple keymap are used to generate subkeys. However, the keymap
and key subkey generating function are both modular and can be adjusted by the user to
see how it affects the encryption/decryption process.
Furthermore, while the default Round function simply returns the XOR (exlusive or) of a given
rounds right side and corresponding subkey.
Finally, users have the option to view verbose output from each round in the Feistal network
including the left side, right side, and subkey used. If the users choses to, this information
can be used to 'walk through' the algorithm step-by-step to assist the learning process.
Examples:
Encryption: ciphertext = encrypt_msg('10101010', 'Super-ultra-mega-secrets-stuff', verbose=True)
Decryption: plaintext = decrypt_msg('10101010', ciphertext, verbose=False)
"""
"""Constants to play with. Try messing with the key or keymaps to see how it affects each round!"""
KEY = '10101010'
SUBKEY_0_MAP = '01010101'
SUBKEY_1_MAP = '10101010'
"""Helper functions used by the Feistal network
Returns 8-bit represenation of character"""
def char_to_binary(char):
return bin(ord(char))[2:].zfill(8)
"""Returns corresponding character from 8 binary bits"""
def binary_to_char(bits):
return chr(int(bits, 2))
"""Returns binary version of plaintext"""
def plaintext_to_binary(pt):
binary = ''
for letter in pt:
binary = binary + char_to_binary(letter)
return binary
"""Returns a list of subkeys from a binary primary key and keymap
Note: This key generator would never be used in a production. Feel free to
make up your own key generator and see how it affects the encryption/decryption
process.
"""
def gen_subkeys(key):
return [SUBKEY_1_MAP if int(bit) is 1 else SUBKEY_0_MAP for bit in KEY]
"""Returns 16-bit 'blocks' of plaintext for encryption/decryption"""
def blockify_msg(msg):
# List to store message blocks
blocks = []
# Pad the end of odd length strings
if len(msg) % 2 != 0:
msg = msg + ' '
while len(msg) != 0:
# Append 16-bit block to list of blocks
blocks.append(msg[:2])
# Remove 16-bit block from original message
msg = msg[2:]
return blocks
"""Returns 8-bit result of exclusive for a given input and mask"""
def xor(_input, mask):
return bin(int(_input, 2) ^ int(mask, 2))[2:].zfill(8)
"""Performs round function on a given right side input and subkey
This is a great place to test how different round functions affect the
encryption/decryption process.
Try it out! What happens if the round function always returns an 8-bit
string of 1's? What about the compliment of the right side input?
"""
def round_function(right, subkey):
return xor(right, subkey)
"""Returns a dictionary representing the left and right side
after a given round in the Fiestal network.
"""
def feistal_round(left, right, subkey):
return {
'left': right,
'right': xor(round_function(right, subkey), left)
}
"""Runs a 16-bit 'block' through the Feistal network for encryption
or decryption
:param subkeys: List of 8 binary, 8-bit subkeys for each round
:param block: 16-bit string to be run through the Feistal network
:param verbose: Boolean flag for displaying detailed output each round
:return: String representation of block after encryption/decryption
"""
def encrypt_decypt_block(subkeys, block, verbose=False):
# Convert block into binary
binary_block = plaintext_to_binary(block)
# Grab the original left/right side (L0 and R0)
L0 = binary_block[:8]
R0 = binary_block[-8:]
# Display original binary represenation of block
if verbose:
print 'Original left: ', L0
print 'Original right: ', R0
# Prepare the datastructure for the Feistal rounds
round_n = {
'left': L0,
'right': R0
}
# Run the input through 8 rounds in the Feistal network
for i in range(8):
round_n = feistal_round(round_n['left'], round_n['right'], subkeys[i])
if verbose:
print "\nRound: ", i
print "Left" + str(i) + ": " + round_n['left'] + ", Right" + str(i) + ": " + round_n['right'] + ", Key" + str(i) + ": " +subkeys[i]
# Flip the left and right side
feistal_network_output = binary_to_char(round_n['right'])+binary_to_char(round_n['left'])
# Display the original plaintext and its corresponding ciphertext
if verbose:
print '\nPT left: ', L0, binary_to_char(L0)
print 'CT left: ', round_n['left'], binary_to_char(round_n['left'])
print 'PT right: ', R0, binary_to_char(R0)
print 'CT right: ', round_n['right'], binary_to_char(round_n['right']), '\n'
return feistal_network_output
"""Encrypts a message using a Feistal network
:param key: Binary 8-bit key used to generate subkeys
:param msg: String plaintext to be encrypted
:param verbose: Boolean flag for displaying detailed output each round
:return: String of resulting ciphertext from encryption
"""
def encrypt_msg(key, msg, verbose=False):
# Get a list of subkeys for encryption
subkeys = gen_subkeys(key)
# Break the message into 16-bit 'blocks'
blocks = blockify_msg(msg)
ct = ''
# Encrypt each 16-bit block and construct the ciphertext
for block in blocks:
ct = ct + encrypt_decypt_block(subkeys, block, verbose=verbose)
return ct
"""Decrypts a message using a Feistal network
:param key: Binary 8-bit key used to generate subkeys
:param msg: String ciphertext to be decrypted
:param verbose: Boolean flag for displaying detailed output each round
:return: String of resulting plaintext from decryption
"""
def decrypt_msg(key, msg, verbose=False):
# Get a list of subkeys for decyption
# Note: Decryption uses the same subkeys as encryption only in reverse order
subkeys = [subkey for subkey in reversed(gen_subkeys(key))]
# Break the message into 16-bit 'blocks'
blocks = blockify_msg(msg)
pt = ''
# Decrypt each 16-bit block and construct entire plaintext
for block in blocks:
pt = pt + encrypt_decypt_block(subkeys, block, verbose=verbose)
return pt
# Sample run
ciphertext = encrypt_msg(KEY, "Super ultra secret stuff!")
plaintext = decrypt_msg(KEY, ciphertext)
print 'Ciphertext: ', ciphertext
print 'Plaintext: ', plaintext
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment