[crypticl-cvs] CVS crypticl/src

tskogan tskogan at common-lisp.net
Wed Jan 24 21:45:12 UTC 2007


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)))




More information about the Crypticl-cvs mailing list