One-time pad encryption is a theoretically perfect method for encrypting and decoding secret messages. The message is combined character-by-character by modular arithmetic with a "one-time key" which is at least as long as the message. For example, if the first character of the message is "E" (5th letter of the alphabet) and the first character of the key is "L" (12th letter of the alphabet), the first character of the cipher will be: (5+12)="Q". The one-time key is a uniformly random sequence of characters held by both the sender and receiver. Pads must be easy to conceal or destroy. Check out this website for a photo of a Russian one-time pad.
One-time pad encryption is "perfect" in the sense that as long as the key is truly random (and kept secret), the cipher gives absolutely no information about the original message, except for its maximum length. Given the cipher (but not the key), one can generate a possible key to transform the cipher into any arbitrary (intelligible) message. However, as the name suggests, each key may be used only once. If a key is used twice, the key can be broken relatively easy by making some basic assumptions about the underlying message (natural language, letter frequency, etc).
Since one-time pad ciphers are completely secure without access to the key, messages can be transmitted in the open. It has been speculated that so-called "numbers stations" transmitting across the shortwave band use one-time pad cyphers. Numbers Stations were the subject of a recent episode of the incredible and thought-provoking independent radio program 99% Invisible. Perfect data cryptography allows these stations to broadcast seemingly random streams of numbers out to secret agents, real or imagined, across the globe.
If you find yourself involved in a clandestine operation, perhaps you will find my Python script useful. All you need is a text file with your one-time keys (a single file can hold as many keys as you like, see sample for details). After specifying the padfile and which key to use, the program accepts the cipher or plaintext via STDIN. Run the program with the "-h" command line option for help and examples. The script uses a 38 character cipher (A-Z,0-9,=,/) where "=" is used in place of a space (" ") and "/" is used in place of a period ("."). Any other characters will be ignored and stripped from the message.
#!/usr/bin/env python import sys import argparse ################################# # Parse command line arguments ################################# parser = argparse.ArgumentParser(description='''Encrypt or decrypt text using a one-time pad cipher. EXAMPLES: ./padcipher.py -p pad -k key -t plaintext.txt > cipher.txt ./padcipher.py -p pad -k key < plaintext.txt > cipher.txt cat cipher.txt | ./padcipher.py -p pad -k key -d > plaintext.txt''',epilog='If no input is supplied you can enter your message directly on the command line. Press RETURN then CTRL+D when done.',formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument('-p','--padfile', help='file containing keys (one-time pad)', required=True); parser.add_argument('-k','--keyname', help='name of the specific key to use', required=True); parser.add_argument('-d','--decrypt', action='store_const', dest='direction',const='d', default='e', help='encrypt the input text (default: encrypt)') parser.add_argument('-t','--text', help='file containing the text to be either encrypted or decrypted (the plaintext or the cipher)') args = parser.parse_args() # Try to load the key file pf = open(args.padfile) key = "" foundKey = False while not foundKey: line = pf.readline() if not line: print "ERROR: Pad not found in key file" pf.close() sys.exit(1) if line == '#' and line[1:].strip() == args.keyname: foundKey = True while True: line = pf.readline() if line and line != "#": key = key + line.strip().replace(' ', '') else: break pf.close() # Check to see if a text file was specified if type(args.text) is str: t = open(args.text) else: t = sys.stdin # print "Enter your message (Press RETURN then CTRL+D when done):" inText = t.read().strip() t.close() # Check if the message is too long if len(inText) > len(key): print "ERROR: Message is longer than key" sys.exit(0) charMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=/" # Preprocess text inText = inText.upper() if args.direction == 'e': # Convert spaces to '=' and periods to '/' inText = inText.replace(' ','=') inText = inText.replace('.','/') else: inText = inText.replace(' ','') # Apply one-time pad cipher outText = "" i = 0 if args.direction == 'e': sgn = 1 else: sgn = -1 for c in inText: if c in charMap: outCharNum = (charMap.index(c) + sgn*charMap.index(key[i])) % len(charMap) outText += charMap[outCharNum] i += 1 # Postprocess text if args.direction == 'd': # Convert '=' to space and '/' to period outText = outText.replace('=',' ') outText = outText.replace('/','.') print outText
Sample key file:
#K1 =K2KV 3POH3 OUQR1 E/FGS C00SD B7QMW Y8UD= O/671 QKHNO JSW49 ZZGRU 8EG00 TKETS WPYAH 0TJEP R8S5K THVPO LQWBN XN1MK 3OB0T YUFNZ NA=X9 M1ILO 00/E/ /31G1 7U0A1 4FNOT #K2 YC359 /U9KX 8X6Y7 V3PD6 H2D7S 6ZYYZ 44HG8 5UPKN =ETUK 534WU QJ1BT E2X58 SLLQR 307KD SPMDC 5PJ4G AYJYB AFK0L 8BKE2 N8VLY KG5V= 7I2U= 5JBJ6 4597O R1WBN XJ0M8 1S9PY #K3 1IPI7 42LSU DYUI1 ZGU35 ME59A DGPOZ OVNHP VT8W4 =HP6B 2IS12 M7E9Q 9E269 TKW3E HPTTF GVCTF OXSGZ JQU9M B2REU 7=7W9 F55VS YNW51 LHMZ6 KG524 MURYE =SBK= LWNJB XC4JM