Cryptography in Python

python

#1

This is something I've been studying and working on for a while now. It's the Vigeneré Cipher, what is the vigeneré? It's basically like a caesar cipher for every single letter in a message.

Further reading: https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher

I wrote it in Python 3, and it uses both the ceasar cipher and a specific key, anyway here it is :point_down::grin:.

#imports
import collections

import string


#caesar cipher
def caesar_cipher(text, shift):
    #all lowercase letters in the alphabet
    alphabet = collections.deque(string.ascii_lowercase)
    #key
    alphabet.rotate(-shift)
    #lowercase alphabet string
    alphabet = "".join(list(alphabet))
    
    #map every letter to the alphabet var
    return text.translate(str.maketrans(string.ascii_lowercase, alphabet))

#vigenere cipher
def vigenere(sent, key):
    try:
        #list comprehension
        sent = [x for x in sent]
        #list comp to encrypt the text based on the shift and the key
        encrypt = [caesar_cipher(sent[x], key[x]) for x in range(len(sent))]
        return ' '.join(encrypt)
    except IndexError:
        raise IndexError("Key is shorter or longer than the message.")

Feel free to give me advice/critique on what I should change or improve. :slight_smile:


#2

Bandit, my friend, you are a genius.
I like this a lot.
You know that I was trying to look into this for a while, and I got the Caesar Cipher done, but never the Vigenere. I love how short and concise this is. I might work on mine some more in the future, using user input. :slight_smile:


#3

Ahaha far from it, but thank you very much. :blush::laughing:

Trying to implement that rn, only problem is the key for the vigenere,

Please show me what you come up with! :grin:


#4

yeah, this is a difficult task, honestly. ionatan gave good input on another topic of mine regarding how to use the key, but i never really made progress because i was tired of working on it. good work though!


#5

Use the list constructor instead?

That's nice. A little heavy to do once for each character, but perhaps that's beside the point

zip function maybe? (indexes are nasty) You'd get as many as the shortest and could do away with the exception, unless you specifically want that exception


#6

Applying a function to each position in data is exactly what the map function does:

def vigenere(msg, key):
    return ''.join(map(caesar_cipher, msg, key))

It's kind of like simplifying expressions in math class, taking your function and ending up with this by combining and eliminating stuff.

print(vigenere('abc', range(3))) # ace

#7

OMG!! Mind blown, :scream:

You have just helped me loaadss! :laughing: Thank you @ionatan for your appreciated input.


#8

Hmmm actually I will try and implement that, although I'll do some reading first seeing as I have never used the zip function,

If you don't mind me asking, are you implying that in can take the place of the range function?


#9

zip would essentially say "for each pair", instead of "use range to generate indexes and then use those indexes like this" .. and writing that in English also says something about how much more complicated indexes are than just "all pairs". But map is capable of using multiple iterables and taking one of each and using them as arguments to the function, so zip just becomes an intermediary step in simplifying the function.


#10

Though I think I will try both ways jut for kicks and also weigh out the pros and cons,

Well @ionatan, I must say when it comes to simplicity you take the cake. :smile: Happy New Year btw!


#11

I only cleaned it up, same as you'd change -1+2-1*3+4 to 2, it's still the same action of "apply the function for each character" which you came up with.

If you first replace the indexes with zip, then put it all on the same line - now you've got the same thing except you've implemented map, and the last change is then to use map instead of writing it


#12

I see it's the same way writing,

def func():
    return [x for x in range(0, 100, 2) print x]

can also be written,

def func():
    for x in range(0, 100, 2):
        print x