OC: Original Cipher (Shift Cipher aka Caesar Cipher)

Todd Brown
Nerd For Tech
Published in
3 min readMar 25, 2021

I am deepening my understanding of cryptography. While I probably should have started this 20 years ago (or even in 2014 as Bit Coin started making noise), there is no time like the present to continue learning. One of the texts I have selected to start with is Understanding Cryptography. The plan is to get my hands dirty and code along with the text (to prove to myself a minimal competency). My weapon of choice (over the past three years) is JavaScript — and while it was up to the task of the first cipher it wasn’t without challenges.

The algorithm takes an alphabet, an offset, and text and replaces each character in the text with a value from the alphabet shifted by the number of characters in the offset. If the offset pushes past the end of the alphabet it wraps back around to the beginning. To encrypt we take a character and find its numerical value (let’s call that x) add to that the offset (let’s call that k) and modulus divide by the number of characters in the alphabet (let’s call that m). Therefore

encrypt(x) = (x + k) mod m

Decrypt is similar

decrypt(x) = (x - k) mod m

The triviality in the solution (which I confirmed by modelling in Excel first) was blown apart 10 minutes into the exercise, it turns out there is some ambiguity around modulo with regard to negative numbers. There are two implementations and if you are not using the right one, things don’t work. As it turns out the javascript modulo operator isn’t the correct implementation for this use case. Having this be the first time I have encountered this modulo oddness it took some research to resolve (thanks wikipedia). I defined my own modulo function:

const quotient = (dividend,divisor) => 
Math.floor(dividend/divisor)

const floor_modulo = (dividend,divisor) =>
dividend - divisor * quotient(dividend,divisor)

with a host of other helpers (to keep my algorithm easy to read) I ended up with this solution:

const {reduce, pipe, curry, find} = 
require('../helpers/functional-bits')
const {maxOr, increment, appendToObj, appendToArray, join, length,
floor_modulo, numberOfKeys, values} = require('../helpers/common-bits')

const maxOrNegOne = maxOr(-1)

const alphabet = 'abcdefghijklmnopqrstuvwxyz'

// arrayToAlphabetMap :: [k] -> {k: Number}
const arrayToAlphabetMap = reduce(
(map, a) => appendToObj(map, a, getNextValue(map)),
{}
)

// getNextValue :: {character: Number} -> Number
const getNextValue = pipe([
values,
maxOrNegOne,
increment
])

// enryptCharacter :: ({character: Number} -> Number -> Number ->
// character -> character) -> {character: Number} -> Number ->
// Number -> [character] -> [character]

const applyWith = curry((algo, alphabetMap, k, m, xs) =>
reduce(
(arr, x) => appendToArray(
arr, algo(alphabetMap, k, m, x))
,[]
, xs
)
)

// enryptCharacter :: {character: Number} -> Number -> Number ->
// character -> character

const encryptCharacter = curry((alphabetMap, k, m, x) => {
const rawValue = alphabetMap[x]
const shiftValue = floor_modulo((rawValue + k), m)
const shiftCharacter = find(alphabetMap, shiftValue)
return shiftCharacter
})

// decryptCharacter :: {character: Number} -> Number -> Number ->
// character -> character

const decryptCharacter = curry((alphabetMap, k, m, x) => {
const shiftValue = alphabetMap[x]
const rawValue = floor_modulo((shiftValue - k), m)
const rawCharacter = find(alphabetMap, rawValue)
return rawCharacter
})

// shiftCipher :: string, Number ->
// { encrypt :: string -> string, decrypt :: string -> string }

const shiftCipher = (alphabet, k) => {
const alphabetArray = alphabet.split('')
const alphabetMap = arrayToAlphabetMap(alphabetArray)
const m = numberOfKeys(alphabetMap)

return {
encrypt: pipe([
applyWith(encryptCharacter, alphabetMap, k, m),
join
]),
decrypt: pipe([
applyWith(decryptCharacter, alphabetMap, k, m),
join
]),
}
}

const raw = 'attack'
const cipher = shiftCipher(alphabet, 17)

const secret = cipher.encrypt(raw)
//=> rkkrtb

const raw2 = cipher.decrypt(secret)
//=> attack

Please note: Resist the itch — this is not a secure cryptographic algorithm. But please play with the code (you can find it here). Also got Code? I would love to hear from you — drop me a note!

About me

Originally published at https://www.linkedin.com.

--

--

Todd Brown
Nerd For Tech

A 25 year software industry veteran with a passion for functional programming, architecture, mentoring / team development, xp/agile and doing the right thing.