One Time Pad in Elixir

Recently I have been very interested in learning about cryptography. I just finished Crypto by Steven Levy which gives a great background to the crypto field and the advances that enabled the current 'Crypto Craze'. I am also working my way through the Stanford Cryptography Course on Coursera which dives deep on some implementations. Given all that, I thought it would be fun to put some of the ideas and learnings into code, and since I am still working on my Elixir skills, lets do it in that. Elixir is actually a nice language for crypto for two reasons:

1) You can use the underlying Erlang crypto library which has many powerful functions.

2) Crypto lends itself very well to functional programming as you are generally passing messages through a number of transformations.

Some of the functions are pretty elegant when seen in Elixir code.

One Time Pad

In this post we will be implementing a basic one time pad. A one time pad is a fundamental concept in cryptography, as it is a cipher that cannot be cracked. A cipher is a means of obscuring a message so that it cannot be read by someone who intercepts it. Many types of ciphers have been used throughout history, and most have been cracked. The one time pad is unique in that it is not just hard to crack, it is mathematically impossible to crack. Unfortunately the one time pad is not all that practical as the key must be the same length or longer as the message you are wishing to encrypt, and can only ever been used once. In practice this means that if you are able to get someone the key in a secure way, you might as well just give them the message that way too.

Nevertheless, lets implement it.

defmodule OneTimePad do
  def encode(message), do: encode(message, message |> String.length |> generate_key)
  def encode(message, key) when byte_size(message) * 2 == byte_size(key) do
    cypher = message
             |> Base.encode16
             |> :crypto.exor(key)
             |> Base.encode16

    IO.puts "Key: #{key}"
    IO.puts "Cypher: #{cypher}"

    cypher
  end
  def encode(_, _), do: IO.puts "Incorrect Byte Sizes"

  def decode(cypher, key) when byte_size(key) * 2 == byte_size(cypher) do
    raw = cypher
          |> Base.decode16!
          |> :crypto.exor(key)
          |> Base.decode16!

    IO.puts "Cypher: #{cypher}"
    IO.puts "Decoded Message: #{raw}"

    raw
  end
  def decode(_, _), do: IO.puts "Incorrect Byte Sizes"

  defp generate_key(length) do
    :crypto.strong_rand_bytes(length)
    |> Base.encode16
  end
end

Running it:

Interactive Elixir (1.6.0) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> cypher = OneTimePad.encode("The secret lies with Charlotte")
Key: A52D26CAC642EB7F6A603CE37EE4F15FD3EAA1D08FEB6317BADA7FA6D9E2
Cypher: 7401047C0403717174050207737100740074010401737370017C73017102077673047378760572080A767171000B07067573720201007602730D7307
"7401047C0403717174050207737100740074010401737370017C73017102077673047378760572080A767171000B07067573720201007602730D7307"

iex(2)> key = "A52D26CAC642EB7F6A603CE37EE4F15FD3EAA1D08FEB6317BADA7FA6D9E2"
"A52D26CAC642EB7F6A603CE37EE4F15FD3EAA1D08FEB6317BADA7FA6D9E2"

iex(3)> OneTimePad.decode(cypher, key)
Cypher: 7401047C0403717174050207737100740074010401737370017C73017102077673047378760572080A767171000B07067573720201007602730D7307
Decoded Message: The secret lies with Charlotte
"The secret lies with Charlotte"

Whats with all the Base.(de|en)code16! calls? Well that is to make the binaries prinatable in iex. I am curious if anyone has a better way to do it in Elixir - please let me know in the comments.

comments powered by Disqus