Hic inserere motto

A simple bip39 diceware script in common lisp

July 07, 2021 — shinohai

I'm currently bench testing a "Foundation devices" passport hardware Bitcoin wallet, because why not? While the device will happily generate bip39 seeds for you I wanted to test load a bunch of seed phrases generated offline without throwing dice all afternoon and figured a simple lisp script would suffice.

I started with this implementation of diceware. The original author provided no asdf system file or anything, but we don't need that where we're going, so a couple of quick modifications get us where we want to be here. We only need an index of 2048 for our *words* array, so we change that in the script to make it compatible with the standard bip39 wordlist.

(require 'ironclad) (defparameter *words* nil) (defparameter *prng* (ironclad:make-prng :fortuna)) (defun load-words (&optional (wd-list #P "bip39-english.txt")) "Load *words* from bip39-english.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 2048)) (load-words)) (let (words) (dotimes (i n) (push (choose-one) words)) (reverse words)))

Grab the standard bip39 English wordlist from the "Bitcoin Core" Shithub repo, and save it in the same directory as the script naming it "bip39-english.txt".

Load up the script in your lisp REPL (I used SBCL here) and run `choose password` + the number of words you are using for your seed. I'm using 23 words in this example since most hardware wallets will calculate the final word for you.

/usr/local/bin/sbcli --load diceware.lisp REPL for SBCL version 0.1.0 Press CTRL-D or type :q to exit [sbcl]> * (choose-password 23) ("there" "image" "federal" "steak" "renew" "helmet" "attack" "medal" "seat" "game" "return" "way" "shock" "hero" "wolf" "amount" "token" "pluck" "gentle" "evoke" "burst" "siege" "ripple")

The device happily accepted the phrases generated via manual entry, calculated the 24th "checksum" word perfectly, and freed up time for me to continue trying builds of the device firmware. More to come.

Tags: Bitcoin, Lisp, Linux