Caesar's way to messaging

I’m currently in the process of switching companies. On my last day my team presented me with a card. Knowing my knack for riddles and brain teasers, they didn’t write a text directly onto the card, they “encrypted” it and wrote some code with higher order functions. This was the card:

While trying to decipher the code I learned a lot. As you can see it’s quite obvious the cypher used is just a simple Caesar shifted by the current weekday. It was supposed to work on a Thursday, but they screwed up a little bit and it ended up to be working on Tuesdays. I knew the code could be improved, so let’s see how we can change it!

Ceasar

Let’s look first into the cypher. Ceasar cyphers are quite simple. Every letter in the alphabet is being asigned a number. You can shift your letter by a specific number and this way you receive the encoded letter.

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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

e.g. “A” shifted by 3 would be a “D”

To decode it, you will have to shift the encoded message by -3. This way you would recieve “A” again. If you reach the max (26) you start over again. Mathematically you could describe it like this:

EncryptedLetter = (Letter + ShiftCount) % 26 + 1 (plus 1 since we started our Alphabet at 1)

The most simple application of this is ROT13. It’s shifting the letters by 13. This way you can use the same algorithm to encrypt and decrypt the message.

Working with computers, our alphabet is not limited to 26 characters. Instead we have symbol tables. ASCII is the most prominent representative. Considering this table you realize we are using 8 Bit to represent characters. Thus we have 256 different symbols. This changes our method above to:

EncryptedLetter = (Letter + ShiftCount) % 256

Some useful basics

Okay let’s have a look at the above implementation. It’s a bunch of higher order functions. This will be a short introduction into the used functions.

map()

With map() you get access to every single element of a sequence in it's order. It will pass you the current value und you can return any value (as long as the return values are all of the same type). So we could return "foo" every time a three is passed in our sequence and otherwise "bar":

"3456783".map{ $0 == "3" ? "foo" : "bar" }

As a result, it will return a new array with the mapped values.

filter()

As the name implies with filter you can filter for specific elements:

"abcdefa".filter { $0 == "a" }

This will return the new array [ "a", "a"].

reduce()

This is probably the most complex function. Basically it applies a function and and accumulator against every value of an array and then returns the result. The most simple version is using it to sum up all values of an array:

[3, 4, 5].reduce(0,+)

This results in the value 12. We won't dive deeper into this, as we don't need more. In case you are interested into functional programming, there are loads of good posts about it.

Improvements

So what are the limitations of the implementation used above?

  • It only decrypts messages shifted by 3
  • It contains multiple codes
  • It can only contain spaces and characters (before shifting) above ASCII value 64 (which excludes e.g. “(“)

Of course this was partly to make it harder for me to decrypt the message and the message was able to contain unnecessary characters. But what can be done to improve it?

First of all, we want to use the same code to en- and decrypt the message. So the “-” has to be replaced. This can be done by just passing it as an argument. The function description for “-” is (in our case)

(UInt32, UInt32) -> UInt32

Storing it in a variable would look like this:

var op: (UInt32, UInt32) -> UInt32 = (-)

Next let’s remove all duplicate code and reorder it. What are the steps we have to take to en-/decrpyt our message.

  1. Split into single letters
  2. Convert it to ASCII value
  3. shift it according to number
  4. Convert it to Character

Splitting the letters is quite simple using String.unicodeScalars:

msg.unicodeScalars

Next up we get the Unicode value of every single letter:

msg.unicodeScalars.map{$0.value}

This looks a little bit complicated but in itself, it just creates an array of Unicode values by using map.

Shifting the value is simply done by calling:

op(unicodeValue, number)

Putting it all together our function would look like this:

But we’re not done yet. This is fixed to shifting the value by 3. We could change it to pass the value, but that would be boring, so how about passing a password? Since our password will probably be shorter than our message, we will repeat it for de- and encryption. We will shift the value by it’s Unicode value. So “P” would shift by the value 80:

T h i s I s T h e M e s s a g e I t I s S o L o n g
P a s s w o r d P a s s w o r d P a s s w o r d P a

So how do we repeat the password? Quite simple. We use String(repeating: count:). It’s not necessary to have the same length, as long as the passphrase is at least as long as the message:

passphrase = String(repeating:password count: (message.count / password.count) + 1)

Having the passphrase we can feed it into map() via zip():

zip(message_values, passphrase).map{ $0.0, $0.1 }

This is nearly everything we have to do. Just convert the letters of the passphrase to Unicode values and use your operation on it and we’re done. The final version looks like this:

This does not filter out specific characters. It can be done though with ranges quite nicely:

message.filter{[, (64..<91), (96..<123)].joined().contains($0)}

Implementing this into the above function, will be left to you.

Conclusion

It was fun decomposing the code on the card and improving it. I’m sure though, that I didn’t find all improvements possible, so if you find a better version (more cryptic, or clearer, it doesn’t matter) shoot me a message on Twitter. I’m curious how you would do it!