Update of /project/crypticl/cvsroot/crypticl/src In directory clnet:/tmp/cvs-serv5500/src
Modified Files: random.lisp diffie-hellman.lisp Log Message: Replaced secure PRNG based on SHA-1 with 128 bits AES in counter mode. Should be 256 bits, but seems to be a bug in AES key expansion.
--- /project/crypticl/cvsroot/crypticl/src/random.lisp 2007/01/23 23:55:39 1.7 +++ /project/crypticl/cvsroot/crypticl/src/random.lisp 2007/01/24 21:45:12 1.8 @@ -5,125 +5,14 @@ ;;;; Author: Taale Skogan ;;;; Distribution: See the accompanying file LICENSE.
-;;To do: -;;-get high entropy bits on non-Linux system. Either roll your own (most likely bad idea) or use win32API to handle one other system. But this is not important. win32 API CryptGenRandom. - (in-package crypticl)
+;;;; +;;;; INTERNALS +;;;;
-(defun random-secure-bignum (bitsize) - "Return random integer bitsize bits long generatated from a cryptograpically secure pseudo random number generator. The function is very slow because random-secure invokes SHA-1 multiple times. It should only be used for cryptographic keys and the like. - -Note that according to Menezes et al (1997), there exists no formal proof that this add-hoc solution using SHA-1 is cryptographically secure. But it is nevertheless approved for use in standards like the Digital Signature Standard (NIST 2000). -" - (random-bignum-internal bitsize #'random-secure)) - - -(defun random-bignum-internal (bitsize rand-function) - "Internal version of random-bignum. rand-function is a random number generator which takes one integer argument r and returns an integer in the range [0,r-1]." - (assert (> bitsize 0)) - (let* ((size (ceiling bitsize 8)) - (octet-array (make-array size - :element-type '(unsigned-byte 8))) - n) - (do ((i 0 (1+ i))) - ((>= i size)) - (setf (aref octet-array i) (funcall rand-function 256))) - - ;;Remove extra bits if necessary. This is done by setting the - ;;unnecessary 8 - (bitsize mod 8) most significant bits to zero. - (setf n (octet-vector-to-integer octet-array)) - (if (= 0 (mod bitsize 8)) - n - (dpb 0 (byte (- 8 (mod bitsize 8)) bitsize) n)))) - - -(defun random-bignum-max-odd (bitsize) - "Return random, bitsize bits long, odd integer. In other words, the least and most significant bit is always 1." - (let ((n (random-secure-bignum bitsize))) - (setf n (dpb 1 (byte 1 (1- bitsize)) n) - n (dpb 1 (byte 1 0) n)))) - - - -;;;;;;;; CRYPTOGRAPHICALLY SECURE RANDOM NUMBER GENERATOR - -(defparameter *random-secure-obj* nil - "State for the random number generator") - -(defun random-secure-range (low high) - "Return random integer in the range [low,high]. This should only be used for values of low close to 0." - (while t - (let ((n (random-secure (+ high 1)))) - (when (>= n low) - (return-from random-secure-range n))))) - - -(defun random-secure (n) - "Return random integer in the range [0,n-1]." - (if (not *random-secure-obj*) - (setf *random-secure-obj* (make-SecurePRNG))) - (SecurePRNG-random *random-secure-obj* n)) - -(defun random-secure-octets (size) - "Returns size pseudorandom octets from a cryptographically secure PRNG." - (if (not *random-secure-obj*) - (setf *random-secure-obj* (make-SecurePRNG))) - (SecurePRNG-octets *random-secure-obj* size)) - -(defclass SecurePRNG () - ((seed :accessor seed - :initarg :seed)) - (:documentation "Cryptographically secure pseudo random number generator.")) - -(defun set-seed (seed) - "Creates a new seed for the secure PRNG. Input should be a high entropy bignum at least 160 bits long." - (if (not *random-secure-obj*) - (setf *random-secure-obj* (make-SecurePRNG))) - (setf (seed *random-secure-obj*) seed)) - -(defun make-SecurePRNG () - "Constructor for the Secure-PRNG class. Assumes that a 160 bits secret/seed is enough." - (make-instance 'SecurePRNG - :seed (random-secure-seed 160))) - - -(defmethod SecurePRNG-random ((obj SecurePRNG) n) - "Return random number in the range [0,n-1] where n <= 2^160. Is terrible inefficient for small n because we waste most of the 160 bits SHA-1 returns." - (assert (<= (integer-length n) 160)) - (let* ((seed (seed obj)) - (state (octet-vector-to-integer - (sha-1-on-octet-vector (integer-to-octet-vector seed))))) - - ;;update seed to get forward security - (setf (seed obj) (mod (+ 1 seed state) - (expt 2 160))) - - ;;reduce n to proper size - (mod state n))) - - -(defmethod SecurePRNG-octets ((obj SecurePRNG) size) - "Returns size pseudorandom octets from a cryptographically secure PRNG." - (let ((rounds (ceiling size 20)) ;20 octets in 160 bits - (ret (make-array size :element-type '(unsigned-byte 8))) - (current-size 0) ;num octets processed so far - (hash nil)) - - (dotimes (i rounds) - (setf hash (sha-1-on-octet-vector (integer-to-octet-vector (seed obj)))) - (do ((j 0 (1+ j))) - ((or (>= current-size size) (>= j 20))) - - (setf (aref ret current-size) (aref hash j)) - (incf current-size)) - - ;;update seed to get forward security - (setf (seed obj) (mod (+ 1 (seed obj) (octet-vector-to-integer hash)) - (expt 2 160)))) - ret)) - - +(defparameter *random-secure-state* nil + "State for the secure random number generator")
(defun high-entropy-octets (size) "Return size octets from some hopefully high entropy bit source." @@ -132,66 +21,38 @@ :if-does-not-exist nil) (if (not file) (progn - (warn "/dev/random was not available on this system.") - (warn "Add 160 bits high entropy seed maually using set-seed.") - (warn "Will use random in the mean time.") + (warn "Unable to get high entropy bits for seeding the secure") + (warn "random number generator. Seed with at least 256 high") + (warn "entropy bits by calling reseed-secure-prng. Will continue") + (warn "in NON-SECURE mode in the mean time.") (do ((i 0 (1+ i))) ((>= i size)) - (setf (aref ret i) (random 256)))) + (setf (aref ret i) (random 256)))) - (progn - (do ((i 0 (1+ i))) - ((>= i size)) - ;;Fails if /dev/random runs out of bytes, but that - ;;should never happen. - (setf (aref ret i) (read-byte file nil))))) - ret))) - - - -(defun random-secure-seed (bitsize) - "Returns bitsize integer with full entrophy (enthropy equals bitsize). Only works on Linux-like systems where /dev/random is a source of high enthropy bits." - (let* ((size (ceiling bitsize 8)) - (octet-array (high-entropy-octets size)) - n) ;Return value + (do ((i 0 (1+ i))) + ((>= i size)) + (setf (aref ret i) (read-byte file nil)))))
- ;;Remove extra bits if necessary. This is done by setting the - ;;unnecessary 8 - (bitsize mod 8) most significant bits to zero. - (setf n (octet-vector-to-integer octet-array)) - (if (= 0 (mod bitsize 8)) - n - (dpb 0 (byte (- 8 (mod bitsize 8)) bitsize) n)))) - - - -;;;; -;;;; AES version -;;;; -;;;; Based on Fortuna from Practical Cryptography. -;;;; -(defparameter *random-secure-obj-aes* nil - "State for the random number generator") + ret))
-(defun random-secure-octets-aes (size) - "Returns size pseudorandom octets from a cryptographically secure PRNG." - (unless *random-secure-obj-aes* - (setf *random-secure-obj-aes* (make-SecurePRNG-AES))) - - (SecurePRNG-octets-aes *random-secure-obj-aes* size)) +;;; +;;; AES related code +;;; +;;; Based on Fortuna from Practical Cryptography. +;;;
(defclass SecurePRNG-AES () ((key :accessor key - :initform #16(0)) + :initform #16(0)) ; TODO use 32 bytes (256 bits) (ctr :accessor ctr - :initform #16(0))) + :initform #16(0))) ; TODO use 32 bytes (256 bits) (:documentation "Cryptographically secure pseudo random number generator."))
(defun make-SecurePRNG-AES () "Constructor for the Secure-PRNG class. Assumes that X bits secret/seed is enough." (let ((obj (make-instance 'SecurePRNG-AES))) - (format t "ctr after init = ~A~%" (hex (ctr obj))) (reseed obj (high-entropy-octets 16))))
(defmethod reseed ((obj SecurePRNG-AES) new-seed) @@ -203,54 +64,91 @@ (setf (key obj) (subseq (hash hasher new-seed) 0 keysize)) ;; We run in counter mode so update counter (inc-counter obj) - (format t "ctr in reseed = ~A~%" (hex (ctr obj))) obj))
(defmethod inc-counter ((obj SecurePRNG-AES)) (int-as-octet-vector-add (ctr obj) 1))
-(defun set-seed-aes (new-seed) +(defmethod SecurePRNG-octets-aes ((obj SecurePRNG-AES) size) + "Returns size pseudorandom octets from a cryptographically secure PRNG." + (let* ((res (make-array size + :element-type '(unsigned-byte 8) + :initial-element 0)) + (ctr-size (length (ctr obj))) + (tmp (make-array ctr-size + :element-type '(unsigned-byte 8) + :initial-element 0))) + (do* ((offset 0 (+ offset next)) + (leftover size (- leftover next)) + (next (min ctr-size leftover) (min ctr-size leftover))) + ((<= leftover 0)) + ;; the cipher overwrites the input buffer so we cannot use + ;; (ctr obj) directly. + (octet-vector-copy (ctr obj) 0 ctr-size tmp 0) + (aes-crypt-octet-vector tmp (key obj) 'ctr-onetime nil) + (octet-vector-copy tmp 0 next res offset) + (inc-counter obj)) + + res)) + + +;;;; +;;;; API +;;;; + +(defun random-secure-octets (size) + "Return size octets from a cryptographically secure PRNG." + (unless *random-secure-state* + (setf *random-secure-state* (make-SecurePRNG-AES))) + (SecurePRNG-octets-aes *random-secure-state* size)) + + +(defun random-secure-bignum (bitsize) + "Return bignum from a cryptographically secure PRNG." + (let* ((size (ceiling bitsize 8)) + (keep (mod bitsize 8)) + (ov (random-secure-octets size))) + ;; Remove extra bits if bitsize not a multiple of 8. + ;; This is done by only keeping the least (bitsize mod 8) significant + ;; bits in the most significant byte. + (unless (= keep 0) + (setf (aref ov 0) (mask-field (byte keep 0) (aref ov 0)))) + (octet-vector-to-integer ov))) + + +(defun reseed-secure-prng (new-seed) "Reseed the global secure PRNG.
The input should be high entropy bits, ideally 256 bits of entropy or more, given as a bignum or a byte array." - (unless *random-secure-obj-aes* - (setf *random-secure-obj-aes* (make-SecurePRNG))) + (unless *random-secure-state* + (setf *random-secure-state* (make-SecurePRNG))) (typecase new-seed - (integer (reseed *random-secure-obj-aes* + (integer (reseed *random-secure-state* (integer-to-octet-vector new-seed))) - (vector (reseed *random-secure-obj-aes* new-seed)))) + (vector (reseed *random-secure-state* new-seed))))
-(defmethod SecurePRNG-octets-aes ((obj SecurePRNG-AES) size) - "Returns size pseudorandom octets from a cryptographically secure PRNG." - (let ((res (make-array size - :element-type '(unsigned-byte 8) - :initial-element 0)) - (tmp (make-array (length (ctr obj)) - :element-type '(unsigned-byte 8) - :initial-element 0)) - (ctr-size (length (ctr obj)))) - - (do* ((offset 0 (+ offset next)) - (leftover size (- leftover next)) - (next (min ctr-size leftover) (min ctr-size leftover))) - ((<= leftover 0)) - ;; the cipher overwrites the input buffer so we cannot use - ;; (ctr obj) directly. - (octet-vector-copy (ctr obj) 0 ctr-size tmp 0) - (aes-crypt-octet-vector tmp (key obj) 'ctr-onetime nil) - (octet-vector-copy tmp 0 next res offset) - (inc-counter obj)) - - res)) + +(defun random-bignum-max-odd (bitsize) + "Return random, bitsize bits long, odd integer. + +In other words, the least and most significant bit is always 1. +Used by RSA and DSA." + (let ((n (random-secure-bignum bitsize))) + (setf n (dpb 1 (byte 1 (1- bitsize)) n) + n (dpb 1 (byte 1 0) n)))) + + +(defun random-secure-bignum-range (low high) + "Return bignum in the range [low,high] from secure PRNG." + ;; Be lazy and retry a few times + (let ((bitsize (integer-length high))) + (do ((n (- low 1)(random-secure-bignum bitsize))) + ((and (<= n high) (>= n low)) n))))
+;;;; TESTING (defun foo () - (setf *random-secure-obj-aes* (make-SecurePRNG-AES)) - (format t "ctr before = ~A~%" (hex (ctr *random-secure-obj-aes*))) - (format t "bytes = ~A~%"(hex (random-secure-octets-aes 16))) - (format t "ctr = ~A~%" (hex (ctr *random-secure-obj-aes*)))) - -(defun bar (&optional (size 16)) - (format t "bytes = ~A~%"(hex (random-secure-octets-aes size))) - (format t "ctr = ~A~%" (hex (ctr *random-secure-obj-aes*)))) + (setf *random-secure-state* nil) + (random-secure-bignum 128)) + \ No newline at end of file --- /project/crypticl/cvsroot/crypticl/src/diffie-hellman.lisp 2007/01/17 22:00:52 1.6 +++ /project/crypticl/cvsroot/crypticl/src/diffie-hellman.lisp 2007/01/24 21:45:12 1.7 @@ -25,7 +25,7 @@ (defmethod generate-random-Diffie-Hellman ((obj Diffie-Hellman)) (let* ((g (g (key obj))) (p (p (key obj))) - (x (random-secure-range 1 (- p 2)))) + (x (random-secure-bignum-range 1 (- p 2))))
(setf (x (key obj)) x) (mod-expt g x p)))