Cryptography with Clojure - One Time Pad

One-time pad is a "perfect" encryption algorithm, when implemented correctly it can not be broken. One-time pad combines the plain text message with a pad (key) using XOR operations. As long as the following factors holds true it is theoretically unbreakable,

  • Pad must be as long as the message.
  • Pad must only be used once.
  • Pad stream must be truly random.

Even though it is theoretically unbreakable, in real world it is pretty much useless because since keys can only be used once, your message security problem turns into key distribution problem. On the plus side since each pad is unique and used only once it is highly resistant to all forms of cryptanalysis.

We begin our implementation by getting some random bytes, for this we will use the SecureRandom class, which provides a cryptographically strong pseudo-random number generator (PRNG), meaning these are truly random values not generated by a mathematical formula.

(defn rand-bytes [size]
  (let [rand (java.security.SecureRandom/getInstance "SHA1PRNG")
        buffer (make-array Byte/TYPE size)]
    (.nextBytes rand buffer) 
    buffer))

Encoding messages, is as easy as XORing the message with the pad we just generated.

(defn encrypt [m]
  (let [message (.getBytes m)
        size (count message)
        pad  (rand-bytes size)
        code (map bit-xor message pad)]
    {:pad (vec pad) :msg (vec code)}))

This will result in a map containing two vectors a pad (key) and the encoded message. Note that the message is binary data if you want to turn it in to a string you need to turn it in to hex string or encode it using Base64.

user=> (encrypt "Attack At Down")
{:pad [-33 21 65 71 94 97 77 5 80 -111 87 100 83 -29], 
 :msg [-98 97 53 38 61 10 109 68 36 -79 19 11 36 -115]}

Decoding the message is even simpler, this time we XOR the pad to the message, and turn each byte into char then concatenate them all.

(defn decrypt [pad message]
  (apply str (map char (map bit-xor pad message))))
user=> (let [message (encrypt "Attack At Down")] 
         (decrypt (:pad message) (:msg message)))
"Attack At Down"

Notice that One-time pad is extremely simple to implement yet it is unbreakable in theory, it's security comes from the protocol not from some complex mathematical function. So you don't want to get it wrong, like the Soviets, which as a result let the U.S. Army's Signal Intelligence Service to read their spies traffic in the Venona program.