2007-02-10

newLISP OTP implementation

I had some free time, so I came up with something fun to play with.

I can't stress enough the importance of protocol when dealing with cryptography. Leaving no trace of the used portion of the pad nor the cleartext file is essential to maintaining the secrecy of your messages.

Some operating systems let you set flags or attributes on files so that they're automatically secure-erased when deleted. Other operating systems require a tool such as "wipe" or "shred" to completely destroy the files. The "leftover" pad can be re-used for future communications. You can make the pad as large or as small as you wish, but it must be larger than the file you wish to encrypt, and it's up to all parties to keep their pads "in sync" with the others. I didn't say OTP was practical, it's just really good when implemented correctly.

I advocate keeping ALL sensitive files (the pad and cleartext) on a USB flash drive along with a secure-erase tool. If the risk of compromise exists, 5-10 seconds in a microwave will render it and all contents completely useless. Although USB flash drives are solid state, given the nature of their construction, it's been proven that some information may be retrievable from them even after a secure erase. Always use your head.

Now that the formalities are out of the way, let's get started!

First, I created a 5kB file of pseudo-random data to use for my pad, and made a copy of the pad for the recipient.

Chimera:~/test ax0n$ dd if=/dev/urandom of=pad bs=1k count=5
5+0 records in
5+0 records out
5120 bytes transferred in 0.001332 secs (3843715 bytes/sec)

Chimera:~/test ax0n$ cp pad receiver-pad
Next, I launched my script. It requires 4 pieces of information. The pad file, the file to encrypt or decrypt, the output file, and the file to write the leftover portion of the pad to. After running it, you can see the script, the leftover pad, the original pad (which should be secure-erased immediately after use), the encrypted target file (in this case, my /etc/passwd file) and the pristine copy of the original pad which should have been handed (never transmitted!) to my recipient.
Chimera:~/test ax0n$ ./crypt.lsp pad /etc/passwd passwd.crypt leftover-pad

Chimera:~/test ax0n$ ls -la
total 56
drwxr-xr-x 7 ax0n ax0n 238 Feb 7 22:36 .
drwx------ 55 ax0n ax0n 1870 Feb 7 22:33 ..
-rwxr-xr-x 1 ax0n ax0n 675 Feb 7 22:33 crypt.lsp
-rw-r--r-- 1 ax0n ax0n 3188 Feb 7 22:36 leftover-pad
-rw-r--r-- 1 ax0n ax0n 5120 Feb 7 22:36 pad
-rw-r--r-- 1 ax0n ax0n 1932 Feb 7 22:36 passwd.crypt
-rw-r--r-- 1 ax0n ax0n 5120 Feb 7 22:36 receiver-pad
Here's part of the hexdump from the encrypted password file. It's very, very random.
Chimera:~/test ax0n$ hexdump -C passwd.crypt
00000000 d3 17 89 23 2f 51 bc 9c c2 3d d3 b3 fb e0 5c 4b |...#/Q...=....\K|
00000010 43 8e 4f e7 71 53 c7 fd 2b b2 ff 37 32 64 3a 2d |C.O.qS..+..72d:-|
00000020 df 8e 19 fa 64 07 c9 8d f2 36 f0 41 e6 ca 65 67 |....d....6.A..eg|
00000030 a3 a4 e6 b0 dc 48 14 08 12 0d c4 72 1a 18 c6 bc |.....H.....r....|
00000040 0a cd 79 69 2b f2 62 15 63 48 da f5 3d 36 41 e8 |..yi+.b.cH..=6A.|

...

00000730 4c 71 17 04 13 50 d2 e0 ab 3a 2d 44 16 0d 72 cf |Lq...P...:-D..r.|
00000740 60 47 16 43 8a 1f 73 03 e8 e9 b8 71 d6 ee fd 68 |`G.C..s....q...h|
00000750 09 dd 81 60 35 65 0b 8f 66 03 97 f4 96 39 78 44 |...`5e..f....9xD|
00000760 f8 24 99 bc 68 6e 28 14 f3 2f fc 0a a3 68 47 70 |.$..hn(../...hGp|
00000770 fd 41 fc 51 e2 c2 0c de 07 be 40 ce 6c a8 bb 5b |.A.Q......@.l..[|
00000780 88 4b 52 50 a4 95 b5 37 71 12 12 76 |.KRP...7q..v|
On the receiving end, we use the receiver-pad file against the encrypted password file, then we can see the new files with ls.
Chimera:~/test ax0n$ ./crypt.lsp receiver-pad  passwd.crypt passwd.clear reciever-leftover-pad

Chimera:~/test ax0n$ ls -la
total 72
drwxr-xr-x 9 ax0n ax0n 306 Feb 7 22:38 .
drwx------ 55 ax0n ax0n 1870 Feb 7 22:33 ..
-rwxr-xr-x 1 ax0n ax0n 675 Feb 7 22:33 crypt.lsp
-rw-r--r-- 1 ax0n ax0n 3188 Feb 7 22:36 leftover-pad
-rw-r--r-- 1 ax0n ax0n 5120 Feb 7 22:36 pad
-rw-r--r-- 1 ax0n ax0n 1932 Feb 7 22:38 passwd.clear
-rw-r--r-- 1 ax0n ax0n 1932 Feb 7 22:36 passwd.crypt
-rw-r--r-- 1 ax0n ax0n 3188 Feb 7 22:38 receiver-leftover-pad
-rw-r--r-- 1 ax0n ax0n 5120 Feb 7 22:36 receiver-pad


Let's see if the decrypted file is legible...


Chimera:~/test ax0n$ cat passwd.clear
##
# User Database
#
# Note that this file is consulted when the system is running in single-user
# mode. At other times this information is handled by one or more of:
# lookupd DirectoryServices
# By default, lookupd gets information from NetInfo, so this file will
# not be consulted unless you have changed lookupd's configuration.
# This file is used while in single user mode.
#
# To use this file for normal authentication, you may enable it with
# /Applications/Utilities/Directory Access.
##
nobody:*:-2:-2:Unprivileged User:/:/usr/bin/false
root:*:0:0:System Administrator:/var/root:/bin/sh
daemon:*:1:1:System Services:/var/root:/usr/bin/false

...

tokend:*:91:91:Token Daemon:/var/empty:/usr/bin/false
securityagent:*:92:92:SecurityAgent:/var/empty:/usr/bin/false
unknown:*:99:99:Unknown User:/var/empty:/usr/bin/false

Yep, it worked!

Let's look at the newLISP code for this project. On the newLISP discussion boards, cormullion and Lutz (the founder of newLISP) reminded me of the beauty of the "cond" command, which works a little bit like "case" except it evaluates whole expressions, not just one variable. I added a bit of whitespace into the code to show how "cond" works. They also helped me re-factor my argument assignment statements down to one line with "map".

#!/usr/bin/newlisp
(cond(
(< (length (main-args)) 5)
(println "USAGE: crypt.lsp [pad] [file] [output] [pad-remainder]")
)
(true
(map set '(pad target output remainder) (rest (rest (main-args))))
(write-file output (encrypt (read-file target) (read-file pad)))
(write-file remainder (slice (read-file pad) (length (read-file target))))
)
)
(exit)


Instead of taking the painstaking route of stepping through all the logic to show how XOR encryption works like I did in my cryptography example a few days ago, I am using as many newLISP shortcuts as I know how to while maintaining the feature set I want. For instance, newLISP has the "encrypt" function which is exactly what I was using in my original example. It runs a bitwise XOR of the contents of two variables, even if those variables contain the entire contents of files! There's no need to explode strings, map the char command, or anything like that. In the above example, newLISP essentially handles all the core logic in this single line:

(write-file output (encrypt (read-file target) (read-file pad)))

It single-handedly reads the pad and target file, XORs them with the encrypt function and writes the resulting garble to the output file.

The line below it figures the length of the target file, skips over that many bytes of the pad file and writes the remaining bytes in the pad out to the remainder file.

Look for more articles like this as I keep playing with newLISP and learning more about it.

blog comments powered by Disqus