Coverage report: /home/ellis/comp/ext/ironclad/src/digests/whirlpool.lisp
Kind | Covered | All | % |
expression | 7 | 286 | 2.4 |
branch | 0 | 8 | 0.0 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
2
;;;; This file implements the Whirlpool message-digest algoritm, as
3
;;;; defined in The WHIRLPOOL Hashing Function, by Paulo S.L.M. Barreto1
4
;;;; and Vincent Rijmen, revised on Revised on May 24, 2003 (1).
6
;;;; It was written by Peter Gijsels.
7
;;;; Copyright (c) 2007, Peter Gijsels
8
;;;; All rights reserved.
10
;;;; This software is "as is", and has no warranty of any kind. The
11
;;;; authors assume no responsibility for the consequences of any use of
15
(eval-when (:compile-toplevel :load-toplevel :execute)
16
(deftype whirlpool-regs () '(simple-array (unsigned-byte 32) (64)))
17
(defun initial-whirlpool-regs ()
18
(make-array 64 :element-type '(unsigned-byte 32) :initial-element 0))
19
(defconstant +whirlpool-regs-hash-offset+ 0)
20
(defconstant +whirlpool-regs-k-offset+ 16)
21
(defconstant +whirlpool-regs-state-offset+ 32)
22
(defconstant +whirlpool-regs-l-offset+ 48)) ; EVAL-WHEN
24
(defconst +pristine-whirlpool-registers+ (initial-whirlpool-regs))
26
(defun whirlpool-regs-digest (regs buffer &optional (start 0))
27
(declare (type whirlpool-regs regs)
28
(type (integer 0 #.(- array-dimension-limit 64)) start))
29
(flet ((stuff-registers (buffer start)
30
(dotimes (i 16 buffer)
31
(setf (ub32ref/be buffer (+ start (* 4 i))) (aref regs i)))))
32
(declare (inline stuff-registers))
34
(buffer (stuff-registers buffer start))
35
(t (stuff-registers (make-array 64 :element-type '(unsigned-byte 8)) 0)))))
37
(eval-when (:compile-toplevel :load-toplevel :execute)
38
(defconstant +whirlpool-rounds+ 10 "The number of rounds. The default is 10."))
40
(eval-when (:compile-toplevel)
41
;;; Code to generate lookup tables +C-EVEN+ and +C-ODD+.
42
(defconst +e+ #(#x1 #xB #x9 #xC #xD #x6 #xF #x3 #xE #x8 #x7 #x4 #xA #x2 #x5 #x0))
43
(defconst +r+ #(#x7 #xC #xB #xD #xE #x4 #x9 #xF #x6 #x3 #x8 #xA #x2 #x5 #x1 #x0))
45
(defun e (i) (aref +e+ i))
47
(defun r (i) (aref +r+ i))
49
(defun e-1 (i) (position i +e+))
51
(defun byte-xor (i1 i2) (logxor i1 i2))
53
(defun s-internal (u v)
54
"The S-box internals. Corresponds to equations on page 10 of (1)."
55
(let ((r (r (byte-xor (e u) (e-1 v)))))
56
(values (e (byte-xor (e u) r))
57
(e-1 (byte-xor (e-1 v) r)))))
61
(let ((u (ldb (byte 4 4) i))
62
(v (ldb (byte 4 0) i)))
63
(multiple-value-bind (u_ v_) (s-internal u v)
65
(setf (ldb (byte 4 4) result) u_
66
(ldb (byte 4 0) result) v_)
69
(defconstant +p8+ #.(reduce #'+ (mapcar #'(lambda (x) (expt 2 x)) '(8 4 3 2 0)))
70
"The primitive polynomial of degree 8 for GF(2^8).")
72
;; Arithmetic in the Galois Field GF(2^8).
81
(loop until (< (integer-length result) (integer-length +p8+))
82
do (setf result (gf-add result (gf-shift +p8+ (- (integer-length result) (integer-length +p8+))))))
87
for i downfrom (integer-length y) to 0
89
(setf result (gf-reduce (gf-shift result 1)))
90
(unless (zerop (ldb (byte 1 i) y))
91
(setf result (gf-add result x))))
92
finally (return result)))
95
"The circulant matrix whose first row is VECTOR."
96
(loop with n = (length vector)
97
with result = (make-array (list n n))
99
do (loop for j below n
100
do (setf (aref result i j) (aref vector (mod (- j i) n))))
101
finally (return result)))
103
(defparameter *c* (cir #(1 1 4 1 8 5 2 9)))
105
(defun calculate-table-word (i j offset)
106
(loop with sx = (s j)
109
do (setf (ldb (byte 8 (- 32 (* (1+ k) 8))) result)
110
(gf-mult sx (aref *c* i (+ k offset))))
111
finally (return result)))
113
(defun calculate-c-even ()
114
(loop with result = (make-array '(8 256) :element-type '(unsigned-byte 32)
118
(setf (aref result i j) (calculate-table-word i j 0)))
119
finally (return result)))
121
(defun calculate-c-odd ()
122
(loop with result = (make-array '(8 256) :element-type '(unsigned-byte 32)
126
(setf (aref result i j) (calculate-table-word i j 4)))
127
finally (return result)))) ; EVAL-WHEN
129
(declaim (type (simple-array (unsigned-byte 32) (22)) +rc+))
131
#.(loop with result = (make-array 22 :element-type '(unsigned-byte 32)
133
with one-row-of-bytes = (make-array 8 :element-type '(unsigned-byte 8))
134
for r from 1 to +whirlpool-rounds+
136
(loop for j below 8 do
137
(setf (aref one-row-of-bytes j) (s (+ (* 8 (- r 1)) j))))
138
(setf (aref result (* 2 r)) (ub32ref/be one-row-of-bytes 0))
139
(setf (aref result (+ (* 2 r) 1)) (ub32ref/be one-row-of-bytes 4)))
140
finally (return result)))
142
(declaim (type (simple-array (unsigned-byte 32) (8 256)) +c-even+ +c-odd+))
143
(defconst +c-even+ #.(calculate-c-even))
144
(defconst +c-odd+ #.(calculate-c-odd))
146
(eval-when (:compile-toplevel :load-toplevel :execute)
147
;;; Macro helper functions.
148
(defun extract-byte (k row column)
150
`(ldb (byte 8 ,(- 24 (* 8 (- column 4)))) (,k ,(1+ (* 2 row))))
151
`(ldb (byte 8 ,(- 24 (* 8 column))) (,k ,(* 2 row)))))
154
(let* ((n (length lst))
160
(defun generate-xor (terms)
161
(if (endp (cdr terms))
163
(multiple-value-bind (terms1 terms2) (split terms)
164
`(logxor ,(generate-xor terms1) ,(generate-xor terms2)))))
166
(defun one-slice (to from i)
167
(let ((indices (loop for n below 8 collect (gensym))))
168
`(let (,@(loop for index in indices
170
collect `(,index ,(extract-byte from (mod (- i j) 8) j))))
172
,(generate-xor `,(loop for index in indices
174
collect `(aref +c-even+ ,j ,index))))
175
(setf (,to ,(1+ (* 2 i)))
176
,(generate-xor `,(loop for index in indices
178
collect `(aref +c-odd+ ,j ,index)))))))) ; EVAL-WHEN
180
(defmacro lookup-in-c (to from)
182
,@(loop for i below 8 collect (one-slice to from i))))
184
(defun update-whirlpool-block (regs block)
185
"this is the core part of the whirlpool algorithm. it takes a complete 16
186
word block of input, and updates the working state in the regs."
187
(declare (type whirlpool-regs regs)
188
(type (simple-array (unsigned-byte 32) (16)) block))
190
`(aref regs (+ ,i +whirlpool-regs-hash-offset+)))
192
`(aref regs (+ ,i +whirlpool-regs-k-offset+)))
194
`(aref regs (+ ,i +whirlpool-regs-state-offset+)))
196
`(aref regs (+ ,i +whirlpool-regs-l-offset+))))
197
;; Compute and apply K^0 to the cipher state
199
do (setf (state i) (logxor (aref block i) (setf (k i) (hash i)))))
200
;; Iterate over all rounds
201
(loop for r of-type (integer 1 11) from 1 to +whirlpool-rounds+
203
;; Compute K^r from K^{r-1}
205
(setf (l 0) (logxor (l 0) (aref +rc+ (* 2 r))))
206
(setf (l 1) (logxor (l 1) (aref +rc+ (+ (* 2 r) 1))))
208
do (setf (k i) (l i)))
209
;; Apply the r-th round transformation
210
(lookup-in-c l state)
212
do (setf (l i) (logxor (l i) (k i))))
214
do (setf (state i) (l i)))))
215
;; Apply the Miyaguchi-Preneel compression function
223
;;; Mid-Level Drivers
224
(defstruct (whirlpool
225
(:constructor %make-whirlpool-digest nil)
226
(:constructor %make-whirlpool-state
227
(regs amount block buffer buffer-index))
230
(regs (initial-whirlpool-regs) :type whirlpool-regs :read-only t)
231
(block (make-array 16 :element-type '(unsigned-byte 32))
232
:type (simple-array (unsigned-byte 32) (16)) :read-only t))
234
(defmethod reinitialize-instance ((state whirlpool) &rest initargs)
235
(declare (ignore initargs))
236
(replace (whirlpool-regs state) +pristine-whirlpool-registers+)
237
(setf (whirlpool-amount state) 0
238
(whirlpool-buffer-index state) 0)
241
(defmethod copy-digest ((state whirlpool) &optional copy)
242
(check-type copy (or whirlpool null))
245
(replace (whirlpool-regs copy) (whirlpool-regs state))
246
(replace (whirlpool-buffer copy) (whirlpool-buffer state))
247
(setf (whirlpool-amount copy) (whirlpool-amount state)
248
(whirlpool-buffer-index copy) (whirlpool-buffer-index state))
251
(%make-whirlpool-state (copy-seq (whirlpool-regs state))
252
(whirlpool-amount state)
253
(copy-seq (whirlpool-block state))
254
(copy-seq (whirlpool-buffer state))
255
(whirlpool-buffer-index state)))))
257
(define-digest-updater whirlpool
258
"Update the given whirlpool state from sequence, which is either a
259
simple-string or a simple-array with element-type (unsigned-byte 8),
260
bounded by start and end, which must be numeric bounding-indices."
261
(flet ((compress (state sequence offset)
262
(let ((block (whirlpool-block state)))
263
(fill-block-ub8-be block sequence offset)
264
(update-whirlpool-block (whirlpool-regs state) block))))
265
(declare (dynamic-extent #'compress))
266
(declare (notinline mdx-updater))
267
(mdx-updater state #'compress sequence start end)))
269
(define-digest-finalizer (whirlpool 64)
270
"If the given whirlpool-state has not already been finalized, finalize it,
271
by processing any remaining input in its buffer, with suitable padding
272
and appended bit-length, as specified by the Whirlpool standard.
274
The resulting whirlpool message-digest is returned as an array of 64
275
(unsigned-byte 8) values. Calling UPDATE-WHIRLPOOL-STATE after a call to
276
FINALIZE-WHIRLPOOL-STATE results in unspecified behaviour."
277
(let ((regs (whirlpool-regs state))
278
(block (whirlpool-block state))
279
(buffer (whirlpool-buffer state))
280
(buffer-index (whirlpool-buffer-index state))
281
(total-length (* 8 (whirlpool-amount state))))
282
(declare (type whirlpool-regs regs)
283
(type (integer 0 63) buffer-index)
284
(type (simple-array (unsigned-byte 32) (16)) block)
285
(type (simple-array (unsigned-byte 8) (64)) buffer))
286
;; Add mandatory bit 1 padding
287
(setf (aref buffer buffer-index) #x80)
288
;; Fill with 0 bit padding
289
(loop for index of-type (integer 0 64)
290
from (1+ buffer-index) below 64
291
do (setf (aref buffer index) #x00))
292
(fill-block-ub8-be block buffer 0)
293
;; Flush block first if length wouldn't fit
294
(when (>= buffer-index 32)
295
(update-whirlpool-block regs block)
296
;; Create new fully 0 padded block
297
(loop for index of-type (integer 0 16) from 0 below 16
298
do (setf (aref block index) #x00000000)))
299
;; Add 256 bit message bit length
300
(loop for i of-type (integer 0 8) from 0 below 8
301
do (setf (aref block (+ 8 i))
302
(ldb (byte 32 (- 256 (* 32 (1+ i)))) total-length)))
304
(update-whirlpool-block regs block)
305
;; Done, remember digest for later calls
306
(finalize-registers state regs)))
308
(defdigest whirlpool :digest-length 64 :block-length 64)