Capstone Project: The Vigenere Cipher
An exercise in data types, Unicode code points and type conversions.
Description
This is the second capstone project in the book Get Programming with Go by Nathan Youngman and Roger Peppe (2018). The "The Vigenere Cipher" project closes off unit two of the book, which covers real numbers, memory-versus-precision trade-offs, big numbers, multilingual text, type conversions and more.
Task
Decipher the given string (see cipherText below), which is encrypted using the Vigenere
cipher (see en.wikipedia.org/wiki/Vigenere_cipher).
The Vigenere cipher is essentially a Caesar cipher (see en.wikipedia.org/wiki/Caesar_cipher)
but with modular shifts instead of fixed shifts. In our scenario the key is "golang" with wrap around.
cipherText := "CSOITEUIWUIZNSROCNKFD"
keyword := "GOLANG"
For a full description of the project, see Get Programming with Go (Youngman, Peppe, 2018).
Method
The first part was to construct the key, i.e. the string "golang" repeating itself for the length
of the text to be deciphered. The key could of course just have been hardcoded into the script but
doing it this way I got to use for-loops with more than one variable, which was good practice.
Before I figured out that the type conversion could be as simple as calling string(),
I constructed a more lengthy switch statement:
switch keyword[j] {
case 71:
repeatedKeyword += "G"
case 79:
repeatedKeyword += "O"
case 76:
repeatedKeyword += "L"
case 65:
repeatedKeyword += "A"
case 78:
repeatedKeyword += "N"
}
which essentially is just a more cumbersome way of converting a uint8 Unicode code point
to a string. Although this way of converting works, it involves a fair bit of
hardcoding, so just calling string() is to be preferred.
Constructing the key proved to be the hardest part, since the actual deciphering is done by just
shifting bytes and code points around. A note for the reader would be that the code point 65
in Unicode corresponds to the capital letter "A". This means that in order to check for wrap arounds
in the alphabet the conditional if statement below is needed:
if (cipherText[i] - (repeatedKeyword[i] - 65)) < 65 {
fmt.Printf("%c", cipherText[i]-(repeatedKeyword[i]-65)+26)
} else {
fmt.Printf("%c", cipherText[i]-(repeatedKeyword[i]-65))
All in all this was another fun exercise.
Output
GOLANGGOLANGGOLANGGOL
WEDIGYOULUVTHEGOPHERS%
Code
package main
import "fmt"
func main() {
cipherText := "CSOITEUIWUIZNSROCNKFD"
keyword := "GOLANG"
var repeatedKeyword string
for i, j := 0, 0; i < len(cipherText); i, j = i+1, j+1 {
if j == 6 {
j = 0
}
repeatedKeyword += string(keyword[j])
}
fmt.Printf("%v\n", repeatedKeyword)
for i := 0; i < len(cipherText); i++ {
if (cipherText[i] - (repeatedKeyword[i] - 65)) < 65 {
fmt.Printf("%c", cipherText[i]-(repeatedKeyword[i]-65)+26)
} else {
fmt.Printf("%c", cipherText[i]-(repeatedKeyword[i]-65))
}
}
}
References
Youngman, Nathan & Peppe, Roger. (2018). Get programming with go. Manning Publications.