Geek as a platform

Simple Vigenere cipher in Python (and 3)

Last part of my series about Vigenere cipher. (3 post in a row? I am proud of myself :-P)

In my previous posts I already showed how to use Vigenere square to encrypt/decrypt text, so this time I'll follow the algebraic method described in the Wikipedia:

'vigenere'

I'll use the same input, same key, and same alphabets as in previous exercises:

mykey:  "WHITE" 
input_text:  "en un lugar dela mancha de cuyo nombre no quiero acordarme" 
Position:           00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 
Ref alphabet(M):    A  B  C  D  E  F  G  H  I  J  K  L  M  N  O  P  Q  R  S  T  U  V  W  X  Y  Z 
Key alphabet(K):    B  C  D  E  F  G  H  I  J  K  L  M  N  O  P  Q  R  S  T  U  V  W  X  Y  Z  A 

(In the reference alphabet,we have shifted the letters one position, as the author did in the book, so K alphabet starts in "B" not in "A"

Encryption:

Now we are going to use numbers instead of the square approach.

If you remember the first post, the foundation of this cipher is the tuple (letter,key):

INPUT: EN UN LUGAR DE LA MANCHA
KEY:   WH IT EWHIT EW HI TEWHIT
TUPLE: ('E', 'W'),('N', 'H'),('U', 'I'),('N', 'T'), ('L', 'E'),('U', 'W'),[...]

Let's get the positions of each element in the tuple in M[] and K[]:

1st tuple: 'E' is in position 4 in our reference alphabet(M). 'W' is in position 21 in the Key alphabet(K)
2nd tuple: 'N' is in position 13 in M[], 'H' is in position 6 in K[]
3rd tuple: 'U' is in position 20 in M[], 'I' is in position 7 in K[]

('4','21'),('13','6'),('20','7'),('13','18'),[...]

So putting this in the mathematical notation:

C[i] = (M[i]+K[i]) mod len(M)

C[0] = (4 + 21) % 26 = 25
C[1] = (13 + 6) % 26 = 19
C[2] = (20 + 7) % 26 = 1
[...]

So the letter "E" in position 4 in M[] will be replaced by the letter in position 25 in K[], which is "A". The same way, the letter "N" in position 13 in M[] will be replaced by the letter in position 19 in K[], which is "U", and so on...

E -> K[25] ->A
N -> K[19] ->U
U -> K[1] ->C
N -> K[5] ->G
L -> K[14] ->P 
U -> K[15] ->Q
G -> K[12] ->N
[...]

Decryption:

Decryption works pretty much the same way... The message is calculated this way:

M[i] = (C[i]-K[i]) mod len(M)

Let's check step by step.. This is our input data:

INPUT: AU CG PQNIK HA SI FEJJPT
KEY:   WH IT EWHIT EW HI TEWHIT
TUPLE: ('A', 'W'),('U', 'H'),('C', 'I'),('G', 'T'), ('P', 'E'),('Q', 'W'),('N', 'H'),('I', 'I'),('K', 'T'),('H', 'E'),[...]

We have to look for the positon of the each letter (of each tuple) in alphabet K[]:

1st tuple: Position of letter "A" and "W" in K[], 25 and 21.
2nd tuple: Position of letter "U" and "H" in K[], 19 and 6.
3rd tuple: C,I -> 1, 7

Now, coming back to the formula:

M[0] = (C[0]-K[0]) mod len(M) = (25 - 21) % 26 = 4 
M[1] = (C[1]-K[1]) mod len(M) = (19 - 6) % 26 = 13  
M[2] = (C[2]-K[2]) mod len(M) = (1 - 7) % 26 = 20 
M[3] = (C[3]-K[3]) mod len(M) = (5 - 18) % 26 = 13 

And looking for those positions in our reference alphabet M[]:

A -> M[4] -> E 
U -> M[13] -> N 
C -> M[20] -> U 
G -> M[13] -> N 
P -> M[11] -> L 
Q -> M[20] -> U 
N -> M[6] -> G 
I -> M[0] -> A 
K -> M[17] -> R 
[...]

Let put this into python code:

#!/usr/bin/env python
import string

mykey="WHITE"
input_text="en un lugar de la mancha de cuyo nombre no quiero acordarme"
code_text="AU CG PQNIK HA SI FEJJPT HA JCRS JVUUVA UW JYELZH EYVZWENTM"

# Alphabet used as reference (M)
# ABCDEFGHIJKLMNOPQRSTUVWXYZ
source = string.ascii_uppercase

# Key alphabet (K) shifted 1 position to the left
# BCDEFGHIJKLMNOPQRSTUVWXYZA
shift = 1
matrix = [ source[(i + shift) % 26] for i in range(len(source)) ]

def coder(thistext):
    ciphertext = []
    control = 0

    for x,i in enumerate(input_text.upper()):
        if i not in source: 
            #If the symbol is not in our reference alphabet, we simply print it
            ciphertext.append(i)
            continue
        else:
            #Wrap around the mykey string 
            control = 0 if control % len(mykey) == 0 else control 

            #Calculate the position C[i] = (M[i]+K[i]) mod len(M)
            result = (source.find(i) + matrix.index(mykey[control])) % 26

            #Add the symbol in position "result" to be printed later
            ciphertext.append(matrix[result])
            control += 1

    return ciphertext

def decoder(thistext):
    control = 0
    plaintext = []

    for x,i in enumerate(code_text.upper()):
        if i not in source: 
            #If the symbol is not in our reference alphabet, we simply print it
            plaintext.append(i)
            continue
        else:
            #Wrap around the mykey string 
            control = 0 if control % len(mykey) == 0 else control 

            #Calculate the position M[i] = (C[i]-K[i]) mod len(M)
            result = (matrix.index(i) - matrix.index(mykey[control])) % 26

            #Add the symbol in position "result" to be printed later
            plaintext.append(source[result])
            control += 1

    return plaintext

# Print results
print("Key: {0}".format(mykey))
print("\nDecode text:")
print("-> Input text: {0}".format(input_text))
print("-> Coded text: {0}".format(''.join(coder(input_text))))

# Print results
print("\nDecode text:")
print("-> Input text: {0}".format(code_text))
print("-> Decoded text: {0}".format(''.join(decoder(code_text)).lower()))

Code stored in GitHub

The script is pretty basic and simple to understand. There are two functions, and the key part is the calculation of result using the math formula shown above.

And see the result:

$ python Vigenere_cipher_mod.py
Key: WHITE

Decode text:
-> Input text: en un lugar de la mancha de cuyo nombre no quiero acordarme
-> Coded text: AU CG PQNIK HA SI FEJJPT HA JCRS JVUUVA UW JYELZH EYVZWENTM

Decode text:
-> Input text: AU CG PQNIK HA SI FEJJPT HA JCRS JVUUVA UW JYELZH EYVZWENTM
-> Decoded text: en un lugar de la mancha de cuyo nombre no quiero acordarme

There are tons of references about how to break this code on the internet. I found these two very interesting:

Later

PS: I hate markdown. Just a little bit...

Simple Vigenere cipher in Python (2)

Just a small update to my previous post about the Vigenere cipher

Following the same approach as in the cipher, I modified a few lines in the script to create the "decoder"... I won't paste the code here, because the script is 95% the same, but I have stored it in GITHUB.

Basically, some variables change:

  • coder: input_text="en un lugar de la mancha de cuyo nombre no quiero acordarme"
  • decoder: input_text="AU CG PQNIK HA SI FEJJPT HA JCRS JVUUVA UW JYELZH EYVZWENTM"

  • coder: ciphertext = []

  • decoder: cleartext = []

  • coder: print("-> Output text: {0}".format(''.join(ciphertext)))

  • decoder: print("-> Output text: {0}".format(''.join(cleartext)))

And the logic for the look up changes as well:

In the coder, we replace the letter in the input_text by the one in the matrix[n] :

for x,y in encryption_tuple:
    if source.find(x) == -1: 
        ciphertext.append(x)
    else:
        ref_row = matrix[0].index(y)
        ciphertext.append(matrix[ref_row][source.index(x)])

In the decoder, we look for the position (lets call it "y") of the letter in matrix[n] and replace it by the letter in the position "y" in the alphabet:

for x,y in encryption_tuple:
    if source.find(x) == -1: 
        cleartext.append(x)
    else:
        ref_row = matrix[0].index(y)
        cleartext.append(source[matrix[ref_row].index(x)])

The result is the expected:

-> Key: WHITE
-> Input text: AU CG PQNIK HA SI FEJJPT HA JCRS JVUUVA UW JYELZH EYVZWENTM
-> Output text: EN UN LUGAR DE LA MANCHA DE CUYO NOMBRE NO QUIERO ACORDARME

In the next (and last post) about Vigenere, I'll write another simple coder/decoder script but based in the mathematical concept

Laters!

Simple Vigenere cipher in Python

I am currently reading "The code book" by Simon Singh, and he just described how the Vigenere cipher works... I am not coding any Python lately, so I have decided to implement it (real quick), not using any algorithm but manually, as someone would have done 300 years ago, preparing a Vigenere square, and then looking up the values in the table.

The Python code is pretty simple:

#!/usr/bin/env python
# Simple Vigenere cipher implementation in Python
import string

mykey="WHITE"
input_text="en un lugar de la mancha de cuyo nombre no quiero acordarme"

ciphertext = []
matrix = []
encryption_tuple= []
row = 0
control = 0

# Alphabet used as reference
source = string.ascii_uppercase

# Creating the Vigenere Square. A 26x26 matrix. 
# In the example provided by the book, instead of using the regular alphabet as reference 
# we shift the items, so the column used as reference doesn't start in A, but in B
for row in range(len(source)):
    matrix.append([ x for i,x in enumerate(source) if i > row ])   
    for i,x in enumerate(source):
        if i <= row: matrix[row].append(x)

# Creating the tuple based on the letter and key. ie:
# ('D', 'W'), ('I', 'H'), ('V', 'I'), ('E', 'T'), ('R', 'E'), ('T', 'W'), ...        
# In case special characters are not considered, this is cleaner:
#   import itertools
#   text=[ x for x in input_text.upper() if x in string.ascii_letters]
#   encryption_tuple = [(x,y) for x,y in zip(text, itertools.cycle(mykey))]
for x,y in enumerate(input_text.upper()):
    control = 0 if control % len(mykey) == 0 else control
    if y in string.punctuation or y in string.whitespace:
         encryption_tuple.append((y,y))
    else:
         encryption_tuple.append((y,mykey[control]))
         control += 1

# Each element y in the tuple is the key in the alphabet matrix
for x,y in encryption_tuple:
    if source.find(x) == -1: 
        ciphertext.append(x)
    else:
        ref_row = matrix[0].index(y)
        ciphertext.append(matrix[ref_row][source.index(x)])

# Print guide
print("-> Reference:")        
print("   " + ' '.join([x for x in source]))
# Printing Vigenere square
print("-> Square:")        
for id,i in enumerate(matrix,1):
    print("{:02d} {}".format(id,' '.join(i)))
# Print results
print("-> Key: {0}".format(mykey))
print("-> Input text: {0}".format(input_text))
print("-> Output text: {0}".format(''.join(ciphertext)))

I stored the code in GITHUB.

In my case, as in the book, I have used "WHITE" as keyword, and the string to cipher is hardcoded in the script (input_text)

The main step is to map the input_text("en un lugar...") with the keyword("WHITE"), so we end up with something like this:

('E', 'W')
('N', 'H')
(' ', ' ')
('U', 'I')
('N', 'T')
(' ', ' ')
('L', 'E')
('U', 'W')
('G', 'H')
('A', 'I')
('R', 'T')
(' ', ' ')
('D', 'E')
[...]
  • The keyword will be repeated over and over until input_text string finishes
  • For the sake of clarity, I haven't stripped the white spaces or any other punctuation symbol from the input message, but it makes the cipher (even) weaker.

Those tuples are going to be used to look for the letter to replace the actual symbol. For example: In order to cipher "E", we will use the row 22, as is the one starting by "W", therefore, the letter "E" will be replaced by "A". Next letter is "N", and we will use row 7, so it will be replaced by "U", and so on...

Not very likely to replace opengpg, but it works:

$ python vigenere_cipher.py
-> Reference:
   A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
-> Square:
01 B C D E F G H I J K L M N O P Q R S T U V W X Y Z A
02 C D E F G H I J K L M N O P Q R S T U V W X Y Z A B
03 D E F G H I J K L M N O P Q R S T U V W X Y Z A B C
04 E F G H I J K L M N O P Q R S T U V W X Y Z A B C D
05 F G H I J K L M N O P Q R S T U V W X Y Z A B C D E
06 G H I J K L M N O P Q R S T U V W X Y Z A B C D E F
07 H I J K L M N O P Q R S T U V W X Y Z A B C D E F G
08 I J K L M N O P Q R S T U V W X Y Z A B C D E F G H
09 J K L M N O P Q R S T U V W X Y Z A B C D E F G H I
10 K L M N O P Q R S T U V W X Y Z A B C D E F G H I J
11 L M N O P Q R S T U V W X Y Z A B C D E F G H I J K
12 M N O P Q R S T U V W X Y Z A B C D E F G H I J K L
13 N O P Q R S T U V W X Y Z A B C D E F G H I J K L M
14 O P Q R S T U V W X Y Z A B C D E F G H I J K L M N
15 P Q R S T U V W X Y Z A B C D E F G H I J K L M N O
16 Q R S T U V W X Y Z A B C D E F G H I J K L M N O P
17 R S T U V W X Y Z A B C D E F G H I J K L M N O P Q
18 S T U V W X Y Z A B C D E F G H I J K L M N O P Q R
19 T U V W X Y Z A B C D E F G H I J K L M N O P Q R S
20 U V W X Y Z A B C D E F G H I J K L M N O P Q R S T
21 V W X Y Z A B C D E F G H I J K L M N O P Q R S T U
22 W X Y Z A B C D E F G H I J K L M N O P Q R S T U V
23 X Y Z A B C D E F G H I J K L M N O P Q R S T U V W
24 Y Z A B C D E F G H I J K L M N O P Q R S T U V W X
25 Z A B C D E F G H I J K L M N O P Q R S T U V W X Y
26 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

-> Key: WHITE
-> Input text: en un lugar de la mancha de cuyo nombre no quiero acordarme
-> Output text: AU CG PQNIK HA SI FEJJPT HA JCRS JVUUVA UW JYELZH EYVZWENTM

I'll try to find time to code the decryption part and also to try different ways of implementing it.

By the way, I totally recommend the book!

Later

SFTP oneliner (as SCP)

Quick note to self:

Secure Copy (scp) (a one line command very easy to include in bash scripts) can be replaced by a similar Secure File Transfer (sftp) command, but the documentation is not very clear...

For instance, this command to transfer a file to a remote host using SCP:

scp src_file user@remote_host:/remote/path/dst_file

Can be replaced by this one, using SFTP:

sftp user@remote_host:/remote/path/ <<< $'put src_file dst_file'

Example:

# sftp root@192.168.1.100:/tmp/ <<< $'put /tmp/test test_sftp'
Connected to 192.168.1.100.
Changing to: /tmp/
sftp> put /tmp/test test_sftp
Uploading /tmp/test to /tmp/test_sftp
/tmp/test                                          100%    0     0.0KB/s   00:00
#

The idea is taken from a few StackOverflow threads, all the credit to them... I decided to put it together here, so I can find it easily.

Later

2017 resolutions

'2017resolutions'

Another entry in my personal online notebook (considering the number of visits of this site, I don't think it should be called blog or website)

We are only 11 day into the new year, so it is not too late for my 2017 resolutions. I'd rather post them here than keep them in a piece of paper on my desktop because, apparently, making goals "public" helps to succeed (or I can be publicly shamed at the end of the year)

Here they go:

1) The classics:

  • Work out more often: At least 3 times per week. No matter if it rains or snows.
  • Eat (more) healthy: It seems I'll be stuck in Spain for a few months (at least until April), so it will be a good opportunity to prepare some green juices (I bought a blender a couple of years ago, and I think I've turned it on 2 times).
  • Read (more) books: Goodreads helps me to track my reading habits... And they are far from impressive, around 12 books per year. It doesn't sound too bad, but considering I don't have family responsibilities, it is not enough. I think I should blame Netflix for this :)
  • Study something new: There are countless online courses nowadays available for free. I've already done a couple of them in edX and I'd like to do more. Coursera has some really interesting courses as well.
  • Improve my English pronunciation: My English is quite good, but still sounds like a spaniard speaking English.

2) Job related:

  • Look for a different role/position: I've been doing pretty much the same stuff for way too long. Time to change. Period.
  • Ramp up my python skills: I am kind of stuck with Python. It is enough for my current role (system engineering in a big telco, not development) but I'd say it is not good enough if I want to do something different (more IT related).
  • Learn some new languages: Go?
  • Ansible: I've been playing with Ansible every now and then for a couple of years, creating some playbooks for myself, but nothing fancy. I don't have the opportunity to use it in my current gig, so I'd need to look for a way to include it in my daily work.

3) Hobby related:

  • Fix my home network: 1 ISP router + 1 AP router + 1 openWRT (as firewall) + 1 NAS + several PC/laptops/smartphones/raspberry pi's = a hell of a mess.
  • Soldering: I have a bunch of ESP8266 chips laying around in a box. I should put them to good use.
  • Photo library housekeeping: I have traveled a lot these past years, and I have tons of photos in my NAS that I have not even seen. It needs a cleanup.
  • Post more (and better): I registered this domain back in 2005... And, even though no one reads this (the number of daily page reads is close to zero), I like to post stuff here. It's a good way to keep things available (thanks to github of course). I'd like to post more stuff, and if possible, more interesting.

I'd say this should be enough for 2017.

\\psgonza