This is a multi-layer problem, and I want to take you through the whole thing rather than just asking about IVs. Bear with me. Also, all code below is in Common Lisp because that's what I'm using, but if there are any complaints, I'll edit to put up pseudocode.
I'm trying to put together a procedure which will produce hard-to-guess session tokens for use in a server implementation. What I've got so far is
(ql:quickload (list :ironclad :cl-base64)) (setf *random-state* (make-random-state t)) (defmethod sha256 ((message integer)) (ironclad:digest-sequence :sha256 (ironclad:integer-to-octets message))) (let ((cipher (ironclad:make-cipher :aes :mode :ecb :key (sha256 (random (expt 2 1024))))) (counter (random (expt 2 512)))) (defun new-session-token () (let ((raw (ironclad:integer-to-octets (incf counter)))) (ironclad:encrypt-in-place cipher raw) (cl-base64:usb8-array-to-base64-string raw :uri t))))
A side question here: what vulnerabilities does this approach have over using AES in counter mode?
The way to use this in counter-mode would be to change the line that defines
... (let ((cipher (ironclad:make-cipher :aes :key (sha256 (random (expt 2 1024))) :mode :ctr :initialization-vector [16-element vector goes here]) ...
My questions are as follows:
- What properties does that IV need? The only one I've seen mentioned is uniqueness, which implies that it doesn't have to be secret.
- Do I need to generate a new one each time I generate a session token?
- What's a good way of generating one?
As per comment by Stephen Touset, Ironclad supports CSPRNG out of the box (more specifically, it supports Fortuna. For my purposes, the above functions can be replaced with
(let ((prng (ironclad:make-prng :fortuna))) (defun new-session-token () (cl-base64:usb8-array-to-base64-string (ironclad:random-data 32 prng) :uri t)))
ironclad:make-prng seems inconsistent in runtime; I've timed between 8 and 70 seconds.