Created
February 15, 2014 23:48
-
-
Save HarryRybacki-zz/9027020 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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