One-time-pad encryption with R and basic JavaScript

One-time-pad encryption offers unbreakable* security if you can guarantee truly random number generation and security of the generated pad. Implementing it from the perspective of a cryptographic layman is a fun way to learn more about it and could add an extra layer of security to your most sensitive communications.

A format of one-time pad used by the U.S. National Security Agency, code named DIANA. The table on the right is an aid for converting between plain txt and cipher text using the characters at left as the key. The black circle is an artifact of copying, due to a hole punched in the original.
(Image and text copied verbatim from: https://en.wikipedia.org/wiki/File:NSA_DIANA_one_time_pad.tiff)

I started off by trying to mimic the NSA example above. I eventually settled on a 169 character key (mapped as a 13 x 13 grid) as I struggled to encode more characters into the QR codes. It still works fine for short messages and as a proof of concept, it’s more than enough.

The R code is short and simple and is provided below. It starts off by generating 13 x 13 random numbers ranging from 0 to 25, to be mapped as A to Z. It also uses the qrcode library to generate a QR code of the encryption key. This is then exported to a PDF file, to be printed and distributed to both the sender and receiver.

#==========================
# Simple one-time-pad generator
# with QR code generation for encryption key
#
# J. van der Linde (jvdl@jvdl.me)
#
# Updated 30 April 2021
#==========================

rm(list = ls())

library(dplyr)
library(qrcode)
library(grid)
library(gridExtra)

#==========================

setwd("~/R/Projects/onetimepad/")

#export pdf to file
pdf("~/R/Projects/onetimepad/otp.pdf")

#initialise with number of rows, cols, and sheets of paper to generate
dim_row <- 13
dim_col <- 13
total_chars <- dim_row * dim_col
total_sheets <- 3

for (i in 1:total_sheets) {
    print(paste("Producing sheet no.", i, "of", total_sheets))
    otp_num <- floor(runif(total_chars, min = 0, max = 26)) #generate from 0 to 25
    otp_char <- replace(otp_num, TRUE, LETTERS[unlist(otp_num + 1)]) #rewrite numbers 0-25 to A-Z
    otp_string <- paste(unlist(otp_char), collapse = '') #create a continuous string for the QR code
    
    otp_num_matrix <- matrix(otp_num, nrow = dim_row, ncol = dim_col, byrow = TRUE) #create a numeric matrix
    otp_char_matrix <- matrix(otp_char, nrow = dim_row, ncol = dim_col, byrow = TRUE) #create a char matrix
    
    otp_char_df <- data.frame(otp_char_matrix) #create dataframe of char matrix
    rownames(otp_char_df) <- seq(dim_row) #set rownames as 1 ... x
    colnames(otp_char_df) <- seq(dim_col) #set colnames as 1 ... y
    
    #output the table of chars to the PDF
    grid.table(otp_char_df)
    grid.text(paste("SHEET", i, "OF", total_sheets), x = unit(0.5, "npc"), y = unit(0.95, "npc"))
    
    #print QR code of OTP to PDF
    qrcode_gen(otp_string)
    
    if (i != total_sheets){
        grid.newpage()
    }
}

#close pdf device
dev.off()

Below is an excerpt from the PDF output. The idea is that two copies of this document should be printed: one for the sender and one for the receiver. The OTP should be handed over in person to ensure it is not intercepted. Both should store the OTP somewhere safe and destroy this electronic copy.

PDF of generated OTP encryption keys and their corresponding QR codes

Once the need arises to send a message with extra confidentiality, our protagonists, let’s call them Alice and Bob in line with tradition, have to agree on a code to use. They may have prearranged to start at the first sheet and continue sequentially. Or they can decide on a sheet spontaneously; for example, Alice can simply send Bob a text saying, “use sheet 3 today”.

In our scenario, let’s assume Alice needs to meet up with Bob. Perhaps they are concluding a confidential transaction or protesting for democracy in their country. She wishes to send the message, “SEE YOU AT ELEVEN AM IN THE TOWN SQUARE”. Firstly she uses a secure instant messenger like Signal to initiate the conversation with Bob.

Alice: Hi FreedomProtestor123. Use sheet 3 today. Message follows soon.

Bob: Hi DemocracyGirl42, will do.

Current events…

By using modular addition (our code is only for A-Z, numbered 0 to 25) Alice calculates the encrypted message. She adds the first letter of the message to the first letter of the key, modulo 26. She repeats for the 2nd letter of the message and the second letter of the key and so on. She then maps the resulting numbers of the modular addition back to letters.

This can also be done fairly easily with the help of computers. Alice could have used her phone to scan the QR code provided next to the old-fashioned grid and completed the process via app or webpage. Alice must ensure she can trust everything in this chain, from the QR code scanner to not having her internet traffic intercepted.

I link here to a basic HTML/JS example which can either be hosted online or stored offline for use. The code is deliberately kept simple and clean (no CSS) to allow easy verification of the source and to decide whether or not to trust it. You can find my implementation hosted at http://jvdl.me/otp.html ; since it is a simple HTML page any phone or computer can render it.

Using the key on sheet 3

(DKPKAREZTPABSCHIBLTHZZLNARDGRXVGIBGQDPWLESQWKKAVQDKSPHZNMRDBBNJPTWDANLOARNELLMGODWAMQPYACBABRVTYAPDQHVQESOWQJESMMMDLZULCVGTCYXVZYIJBKOODXCXYSAUCAFGNUUPIBXPHLSXPBBLTQTLUO)

and her message “SEE YOU AT ELEVEN AM IN THE TOWN SQUARE”, Alice finally ends up with the following encrypted message:

Alice: VOT IOL ES XAEWWP HU JY MOD SZJN JTAROZ

Bob: Got it, thanks.

Mysterious and cryptic; perhaps even romantic…

Alice can drop the spaces to prevent anything being inferred from word lengths. Bob will still be able to figure out the text. Alice could even make use of a generally lesser-used letter (for example Q or X) instead of a space to split words while still maintaining one continuous message. This avoids highlighting individually encrypted words.

Alice does not need the entire key since her message is short. Bob simply repeats this process with subtraction and modulo instead of addition and modulo. He subtracts the key from the encrypted message to derive the original, decrypted message. He now knows when and where to meet up with Alice.

Alice and Bob are both smart people. They keep all of the following in mind:

  • Alice and Bob ensure that they received the only copies of the pads. Alice generated it and handed it to Bob in person, or vice versa.
  • The message should not be longer than the key (i.e. shouldn’t loop around).
  • They need to guard the pads with their lives and store them securely, especially if being found out could cost them their lives.
  • They need to burn the used sheet after encryption/decryption has occurred.
  • They transmit their encrypted message via another secure platform, such as Signal, if possible.

Thanks for following along! Be safe and secure in your communications. You can easily reproduce the work done here, with free software, if you have a really confidential message you’re trying to get across.