I recently downloaded the Ironclad Cryptography Library with quicklisp and wanted to give it a spin. I thought a nice little project would be to implement the Diceware password generation algorithm in Lisp. This turned out to be really easy. I was expecting to use Ironclad primitives to get some random data, expand it with hashing, and use the result as an index to choose a word from the dictionary as I did with the C version. Instead, Ironclad provides the strong-random
function that can provide the indicies with a single function call.
To implement the Diceware algorithm, I first downloaded the diceware8k.txt file that contains 8,192 words. Having the number of words be a power of 2 avoids a bias from the modulo operation—see Diceware Implementation (Part 2)—although strong-random
avoids that at the expense of some extra work. The code itself is very simple:
(in-package #:cl-diceware) (require 'ironclad) (defparameter *words* nil) (defparameter *prng* (ironclad:make-prng :fortuna)) (defun load-words (&optional (wd-list #P "~/Desktop/diceware8k.txt")) "Load *words* with diceware8k.txt." (with-open-file (s wd-list) (do ((wd (read-line s nil) (read-line s nil)) ( i 0 (1+ i))) ((not wd)) (setf (aref *words* i) wd)))) (defun choose-one () "Randomly choose a single word from *words*." (aref *words* (ironclad:strong-random (length *words*) *prng*))) (defun choose-password (n) "Generate n random words for a pass phrase. Initialize *words* if needed" (unless *words* (setf *words* (make-array 8192)) (load-words)) (let (words) (dotimes (i n) (push (choose-one) words)) (reverse words)))
The choose-one
function gets a (strong) random number between 0 and 8191 and uses it as an index into the *words*
array. The choose-password
function loads the dictionary into the *words*
array if it hasn’t already been loaded and then calls choose-one
the requested number of times. The random words are accumulated in the words
local variable and returned at the end.
Here’s a sample call
CL-USER> (cl-diceware:choose-password 6) ("hath" "otto" "fly" "urea" "becalm" "seed")
Choosing 6 words gives 78 bits of entropy, more than enough for today’s hardware and technology.
Update: 6 words gives 72 bits of entropy → 6 words gives 78 bits of entropy