Vigenere CBC


CBC is better than ECB mode, because it makes things more secure, right?

intro puzzle

Challenge script:
import random

alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

def generate_iv(block_size):
    # generates a random string of block_size letters 
    # to be used as the initialization vector for the encryption
    return "".join(random.choices(alphabet, k=block_size))

def add_key(key, plaintext_block):
    key_idxs = [alphabet.index(key_char) for key_char in key]
    pt_idxs = [alphabet.index(pt_char) for pt_char in plaintext_block]
    # adding the indexes of the key and plaintext together
    ct_idxs = [(key_idx + pt_idx) % len(alphabet) for key_idx, pt_idx in zip(key_idxs, pt_idxs)]
    # converting it back into an alphabetical string
    return "".join([alphabet[idx] for idx in ct_idxs])

def vigenere_block_chaining_encrypt(key, plaintext):
    key_length = len(key)
    # pads the plaintext
    plaintext = pad(key_length, plaintext)
    # generates an iv
    iv = generate_iv(key_length)
    # splits the plaintext into blocks of length key_length
    blocks = [plaintext[i:i+key_length] for i in range(0, len(plaintext), key_length)]
    previous_block = iv
    ciphertext = ""
    # refer to https://www.educative.io/answers/what-is-cbc
    for block in blocks:
        # adds the previous block
        # for the first block this will be the random iv
        block = add_key(previous_block, block)
        # adds the vigenere key (the block cipher encryption)
        previous_block = add_key(key, block)
        # adds the output to the final ciphertext
        ciphertext += previous_block
    # give back the plaintext with the iv
    return iv, ciphertext
    
def pad(block_size, plaintext):
    # pads the plaintext with X's until the length is a multiple of block_size
    plaintext += "X" * (-len(plaintext) % block_size)
    return plaintext

SECRET_MESSAGE = "THISISAFAKEMESSAGEYOUNEEDTOUSETHEOUTPUTTOGETTHEREALSECRETMESSAGE"
key = "AFAKEKEY"
assert len(key) == 8
assert all([char in alphabet for char in key])
assert all([char in alphabet for char in SECRET_MESSAGE])

iv, ciphertext = vigenere_block_chaining_encrypt(key, SECRET_MESSAGE)
print("iv =", iv)
print("ciphertext =", ciphertext)
Output:
iv = XCVUSTKK
ciphertext = WYZCAZIXERPEDAJTTMZRCHVVOIEWJXCFMCGHDHKDDMQWDKVRVCCAWNHOWDXHTUTCXPGLDXVQXEIESEITZUQIYHTTZJWKZVUTXKCHLWOCFWGVRQMPFILDXIULREEPMOWJFYGZJFYYFNMRCQWUEZQSOWYITZTWJVMWEACAQJKJLBMOWNSWSMRWCSFZDCOWIUZYNSDCUAXEYYFTRGFMTSVKZMHKSOTOULOGOLGMGRMOVGLTDDIOWBNILNADKNBQRMHXFMGUBLOWUNECFVALJGRYMLEHDMPYDQMRNDFZKCYBFGPMJUZFECEQKMAJYUOJVEBJMCHMSVOTNDXREBMIYNGEFCGPTIMWZVTHHQHANYEWVCJNYTCJKCNRCLEHROHKCORLATGHSKIE