mirror of
https://github.com/pmai/sha3.git
synced 2025-12-21 15:24:28 +01:00
Add initial version of project.
This commit is contained in:
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/*.lisp ident
|
||||||
|
/*.asd ident
|
||||||
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# FASLs
|
||||||
|
*.fas
|
||||||
|
*.fasl
|
||||||
|
*.ofasl
|
||||||
|
*.nfasl
|
||||||
|
*.xfasl
|
||||||
|
*.dx32fsl
|
||||||
|
*.dx64fsl
|
||||||
|
*.abcl
|
||||||
|
*.sse2f
|
||||||
|
# Backup files
|
||||||
|
*~
|
||||||
25
COPYING
Normal file
25
COPYING
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
Copyright (C) 2012 PMSF IT Consulting Pierre R. Mai
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
96
README
Normal file
96
README
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
This library is an implementation of the Secure Hash Algorithm 3
|
||||||
|
(SHA-3), also known as Keccak. The implementation is constrained to
|
||||||
|
messages with an integral number of octets, i.e. sub-byte length
|
||||||
|
messages are not supported.
|
||||||
|
|
||||||
|
The implementation should be portable across nearly all ANSI compliant
|
||||||
|
CL with specialized implementations tuned for implementations that
|
||||||
|
offer unboxed 64bit arithmetic, unboxed 32bit arithmetic and for
|
||||||
|
implementations with efficient fixnum arithmetic (requiring fixnums
|
||||||
|
that can represent (unsigned-byte 16)). Especially the 64 and 32bit
|
||||||
|
implementations have been mostly optimized for SBCL and CMU CL. For
|
||||||
|
those implementations, digests with a 1024 bit-rate (and 288 bit
|
||||||
|
digest output) can be generated in between 30 (64bit SBCL) to around
|
||||||
|
100 (32bit CMU CL) cycles/byte on an i7-640M; whereas optimized
|
||||||
|
C/assembler implementations reach around 12 to 50 cycles/byte on 64/32
|
||||||
|
bit Intel hardware. The reason for the discrepancy probably lies in
|
||||||
|
missing peephole and dependency optimizations in the SBCL/CMU CL
|
||||||
|
compiler backend.
|
||||||
|
|
||||||
|
The mid-level interfaces to the digest routines are the functions
|
||||||
|
|
||||||
|
- sha3:sha3-init &key output-bit-length bit-rate
|
||||||
|
|
||||||
|
Create and return a new SHA-3 state. If `output-bit-length' is
|
||||||
|
specified then the state will run at the bit rate specified for the
|
||||||
|
given output bit length. If `output-bit-length' is unspecified,
|
||||||
|
`bit-rate' can be specified to select a suitable bit rate. If both
|
||||||
|
are left unspecified then a default bit rate of 1024 bits is
|
||||||
|
selected, which is suitable for arbitrary output bit lengths of up
|
||||||
|
to 288 bits.
|
||||||
|
|
||||||
|
- sha3:sha3-copy state
|
||||||
|
|
||||||
|
Return an independent copy of the SHA-3 state `state'.
|
||||||
|
|
||||||
|
- sha3:sha3-state-p state
|
||||||
|
|
||||||
|
Test whether a given object is a SHA-3 state, i.e. is an instance of
|
||||||
|
the class sha3:sha3-state.
|
||||||
|
|
||||||
|
- sha3:sha3-update state vector &key (start 0) (end (length vector))
|
||||||
|
|
||||||
|
Update the given SHA-3 state `state' from `vector', which must be a
|
||||||
|
simple-array with element-type (unsigned-byte 8), bounded by `start'
|
||||||
|
and `end', which must be numeric bounding-indices.
|
||||||
|
|
||||||
|
- sha3:sha3-final state &key output-bit-length
|
||||||
|
|
||||||
|
If the given SHA-3 state `state' has not already been finalized,
|
||||||
|
finalize it by processing any remaining input in its buffer, with
|
||||||
|
suitable padding as specified by the SHA-3 standard. Returns the
|
||||||
|
message digest as a simple-array of (unsigned-byte 8). The length
|
||||||
|
of the returned digest is determined either by the output bit length
|
||||||
|
or bit rate specified on state creation, or for the special case of
|
||||||
|
default parameters being used, by the optional keyword argument
|
||||||
|
`output-bit-length'. If the state has previously been finalized,
|
||||||
|
the function will return the digest again.
|
||||||
|
|
||||||
|
For convenience the following high-level functions produce digests in
|
||||||
|
one step from 1d simple-arrays and streams with element-type
|
||||||
|
(unsigned-byte 8), as well as files:
|
||||||
|
|
||||||
|
- sha3:sha3-digest-vector vector &key (start 0) end (output-bit-length 512)
|
||||||
|
|
||||||
|
Calculate an SHA-3 message-digest of data in `vector', which should
|
||||||
|
be a 1d simple-array with element type (unsigned-byte 8), bounded by
|
||||||
|
`start' and `end'. The bit length of the message digest produced is
|
||||||
|
controlled by `output-bit-length', which can take on the values 224,
|
||||||
|
256, 288, 384 and 512, which is the default value.
|
||||||
|
|
||||||
|
- sha3:sha3-digest-stream stream &key (output-bit-length 512)
|
||||||
|
|
||||||
|
Calculate an SHA-3 message-digest of data read from `stream', which
|
||||||
|
should be a stream with element type (unsigned-byte 8). The bit
|
||||||
|
length of the message digest produced is controlled by
|
||||||
|
`output-bit-length', which can take on the values 224, 256, 288, 384
|
||||||
|
and 512, which is the default value.
|
||||||
|
|
||||||
|
- sha3:sha3-digest-file pathname &key (output-bit-length 512)
|
||||||
|
|
||||||
|
Calculate an SHA-3 message-digest of the file specified by
|
||||||
|
`pathname'. The bit length of the message digest produced is
|
||||||
|
controlled by `output-bit-length', which can take on the values 224,
|
||||||
|
256, 288, 384 and 512, which is the default value.
|
||||||
|
|
||||||
|
Note that in order to generate a message digest of a string it will
|
||||||
|
have to be converted to a simple-array with element-type
|
||||||
|
(unsigned-byte 8) in the proper output-encoding. This will have to
|
||||||
|
rely on implementation-specific functions and is not part of the SHA3
|
||||||
|
library.
|
||||||
|
|
||||||
|
The implementation is licensed under the MIT-style license contained
|
||||||
|
in the file COPYING and the header of each source file.
|
||||||
|
|
||||||
|
Please direct any feedback to pmai@pmsf.de. A git repository of this
|
||||||
|
library is available under git://github.com/pmai/sha3.git
|
||||||
196
common.lisp
Normal file
196
common.lisp
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
;;;; SHA3 --- Secure Hash Algorithm 3 (Keccak) Implementation
|
||||||
|
;;;;
|
||||||
|
;;;; Copyright (C) 2012 PMSF IT Consulting Pierre R. Mai.
|
||||||
|
;;;;
|
||||||
|
;;;; Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
;;;; a copy of this software and associated documentation files (the
|
||||||
|
;;;; "Software"), to deal in the Software without restriction, including
|
||||||
|
;;;; without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
;;;; distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
;;;; permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
;;;; the following conditions:
|
||||||
|
;;;;
|
||||||
|
;;;; The above copyright notice and this permission notice shall be
|
||||||
|
;;;; included in all copies or substantial portions of the Software.
|
||||||
|
;;;;
|
||||||
|
;;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
;;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
;;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
;;;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
;;;; OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
;;;; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
;;;; OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
;;;;
|
||||||
|
;;;; Except as contained in this notice, the name of the author shall
|
||||||
|
;;;; not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
;;;; other dealings in this Software without prior written authorization
|
||||||
|
;;;; from the author.
|
||||||
|
;;;;
|
||||||
|
;;;; $Id$
|
||||||
|
|
||||||
|
(cl:in-package #:sha3)
|
||||||
|
|
||||||
|
;;;; %File Description:
|
||||||
|
;;;;
|
||||||
|
;;;; This file contains common definitions and utility macros/functions
|
||||||
|
;;;; used in the specifically optimized implementations of keccak.
|
||||||
|
;;;;
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Optimization
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(eval-when (:compile-toplevel :load-toplevel :execute)
|
||||||
|
(defvar *optimize-declaration*
|
||||||
|
'(optimize (speed 3) (space 0) (safety 0) (debug 0))
|
||||||
|
"Global optimize declaration used for performance critical functions.
|
||||||
|
This can be changed prior to compiling the package for debugging/testing
|
||||||
|
purposes."))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; LOGXOR Reduction Hack for certain lisps
|
||||||
|
;;;
|
||||||
|
|
||||||
|
#+(or lispworks ccl)
|
||||||
|
(defun logxor (&rest args)
|
||||||
|
(apply #'cl:logxor args))
|
||||||
|
|
||||||
|
#+(or lispworks ccl)
|
||||||
|
(define-compiler-macro logxor (&whole form &rest args)
|
||||||
|
(labels ((binarify (list)
|
||||||
|
(if (rest list)
|
||||||
|
`(cl:logxor ,(car list) ,(binarify (rest list)))
|
||||||
|
(first list))))
|
||||||
|
(if (null args)
|
||||||
|
form
|
||||||
|
(binarify args))))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Partial Evaluation Helpers
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(defun trivial-macroexpand-all (form env)
|
||||||
|
"Trivial and very restricted code-walker used in partial evaluation macros.
|
||||||
|
Only supports atoms and function forms, no special forms."
|
||||||
|
(let ((real-form (macroexpand form env)))
|
||||||
|
(cond
|
||||||
|
((atom real-form)
|
||||||
|
real-form)
|
||||||
|
(t
|
||||||
|
(list* (car real-form)
|
||||||
|
(mapcar #'(lambda (x) (trivial-macroexpand-all x env))
|
||||||
|
(cdr real-form)))))))
|
||||||
|
|
||||||
|
(defmacro dotimes-unrolled ((var limit) &body body &environment env)
|
||||||
|
"Unroll the loop body at compile-time."
|
||||||
|
(loop for x from 0 below (eval (trivial-macroexpand-all limit env))
|
||||||
|
collect
|
||||||
|
`(symbol-macrolet ((,var ,x)) ,@body)
|
||||||
|
into forms
|
||||||
|
finally
|
||||||
|
(return `(progn ,@forms))))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Keccak-f-1600 definitions
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(eval-when (:compile-toplevel :load-toplevel :execute)
|
||||||
|
(defconstant +keccak-state-columns+ 5
|
||||||
|
"Width of Keccak state in the x axis")
|
||||||
|
(defconstant +keccak-state-rows+ 5
|
||||||
|
"Width of Keccak state in the y axis")
|
||||||
|
|
||||||
|
(defconstant +keccak-state-lanes+ (* +keccak-state-columns+ +keccak-state-rows+)
|
||||||
|
"Total number of lanes in Keccak state")
|
||||||
|
|
||||||
|
(defconstant +keccak-1600-lane-width+ 64
|
||||||
|
"Lane width for Keccak-1600.")
|
||||||
|
|
||||||
|
(defconstant +keccak-1600-lane-byte-width+ (truncate +keccak-1600-lane-width+ 8)
|
||||||
|
"Lane width in bytes for Keccak-1600."))
|
||||||
|
|
||||||
|
(deftype keccak-1600-lane ()
|
||||||
|
"Type of a keccak lane for Keccak-1600."
|
||||||
|
`(unsigned-byte ,+keccak-1600-lane-width+))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Keccak Constants
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(defparameter *keccak-f-round-constants*
|
||||||
|
(make-array '(24) :element-type 'keccak-1600-lane
|
||||||
|
:initial-contents
|
||||||
|
#-sha3-fixed-constants
|
||||||
|
(loop with lfrstate = #x01
|
||||||
|
for i from 0 below 24
|
||||||
|
collect
|
||||||
|
(loop with const = 0
|
||||||
|
for j from 0 below 7
|
||||||
|
for bit-position = (1- (ash 1 j))
|
||||||
|
when (logbitp 0 lfrstate)
|
||||||
|
do (setq const (logxor const (ash 1 bit-position)))
|
||||||
|
do (setq lfrstate (if (logbitp 7 lfrstate)
|
||||||
|
(logxor (ldb (byte 8 0) (ash lfrstate 1))
|
||||||
|
#x71)
|
||||||
|
(ash lfrstate 1)))
|
||||||
|
finally (return const)))
|
||||||
|
#+sha3-fixed-constants
|
||||||
|
'(#x0000000000000001
|
||||||
|
#x0000000000008082
|
||||||
|
#x800000000000808a
|
||||||
|
#x8000000080008000
|
||||||
|
#x000000000000808b
|
||||||
|
#x0000000080000001
|
||||||
|
#x8000000080008081
|
||||||
|
#x8000000000008009
|
||||||
|
#x000000000000008a
|
||||||
|
#x0000000000000088
|
||||||
|
#x0000000080008009
|
||||||
|
#x000000008000000a
|
||||||
|
#x000000008000808b
|
||||||
|
#x800000000000008b
|
||||||
|
#x8000000000008089
|
||||||
|
#x8000000000008003
|
||||||
|
#x8000000000008002
|
||||||
|
#x8000000000000080
|
||||||
|
#x000000000000800a
|
||||||
|
#x800000008000000a
|
||||||
|
#x8000000080008081
|
||||||
|
#x8000000000008080
|
||||||
|
#x0000000080000001
|
||||||
|
#x8000000080008008))
|
||||||
|
"Keccak Round Constants")
|
||||||
|
|
||||||
|
(defparameter *keccak-f-rotate-offsets*
|
||||||
|
(make-array (list +keccak-state-columns+ +keccak-state-rows+)
|
||||||
|
:element-type '(unsigned-byte 8)
|
||||||
|
:initial-contents
|
||||||
|
'(( 0 36 3 41 18)
|
||||||
|
( 1 44 10 45 2)
|
||||||
|
(62 6 43 15 61)
|
||||||
|
(28 55 25 21 56)
|
||||||
|
(27 20 39 8 14)))
|
||||||
|
"Keccak Rotate Offsets")
|
||||||
|
|
||||||
|
(defmacro get-rotate-offset (x y &environment env)
|
||||||
|
(aref *keccak-f-rotate-offsets*
|
||||||
|
(eval (trivial-macroexpand-all x env))
|
||||||
|
(eval (trivial-macroexpand-all y env))))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Message Padding for last block
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(defun pad-message-to-width (message bit-width)
|
||||||
|
"Destructively pad the given message to the given bit-width according to
|
||||||
|
Keccak padding rules and return the padded message."
|
||||||
|
(let ((message-byte-length (length message))
|
||||||
|
(width-bytes (truncate bit-width 8)))
|
||||||
|
(setq message (adjust-array message (list width-bytes)))
|
||||||
|
(setf (aref message message-byte-length) #x01)
|
||||||
|
(loop for index from (1+ message-byte-length) below width-bytes
|
||||||
|
do (setf (aref message index) #x00)
|
||||||
|
finally
|
||||||
|
(setf (aref message (1- width-bytes))
|
||||||
|
(logior #x80 (aref message (1- width-bytes))))))
|
||||||
|
message)
|
||||||
510
keccak-16bit.lisp
Normal file
510
keccak-16bit.lisp
Normal file
@ -0,0 +1,510 @@
|
|||||||
|
;;;; SHA3 --- Secure Hash Algorithm 3 (Keccak) Implementation
|
||||||
|
;;;;
|
||||||
|
;;;; Copyright (C) 2012 PMSF IT Consulting Pierre R. Mai.
|
||||||
|
;;;;
|
||||||
|
;;;; Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
;;;; a copy of this software and associated documentation files (the
|
||||||
|
;;;; "Software"), to deal in the Software without restriction, including
|
||||||
|
;;;; without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
;;;; distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
;;;; permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
;;;; the following conditions:
|
||||||
|
;;;;
|
||||||
|
;;;; The above copyright notice and this permission notice shall be
|
||||||
|
;;;; included in all copies or substantial portions of the Software.
|
||||||
|
;;;;
|
||||||
|
;;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
;;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
;;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
;;;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
;;;; OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
;;;; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
;;;; OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
;;;;
|
||||||
|
;;;; Except as contained in this notice, the name of the author shall
|
||||||
|
;;;; not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
;;;; other dealings in this Software without prior written authorization
|
||||||
|
;;;; from the author.
|
||||||
|
;;;;
|
||||||
|
;;;; $Id$
|
||||||
|
|
||||||
|
(cl:in-package #:sha3)
|
||||||
|
|
||||||
|
;;;; %File Description:
|
||||||
|
;;;;
|
||||||
|
;;;; This file contains an implementation of Keccak 1600 tuned to
|
||||||
|
;;;; implementations that support efficient arithmetic on fixnums
|
||||||
|
;;;; which are assumed to be able to represent (unsigned-byte 16)
|
||||||
|
;;;; numbers. NOTE that the ANSI CL standard only guarantees
|
||||||
|
;;;; (signed-byte 16), which would also be sufficient but would
|
||||||
|
;;;; complicate the code somewhat. Since none of the currently useful
|
||||||
|
;;;; implementations have quite so limited fixnums, the assumption
|
||||||
|
;;;; should hold sufficiently well.
|
||||||
|
;;;;
|
||||||
|
;;;; Implementation Choices:
|
||||||
|
;;;;
|
||||||
|
;;;; This is a fairly straightforward implementation of Keccak 1600.
|
||||||
|
;;;; It employs a bit of loop unrolling at compile-time, and splits
|
||||||
|
;;;; the 64bit Keccak 1600 lanes into four 16 bit words with bit
|
||||||
|
;;;; interleaving. It might make sense to test if not using bit
|
||||||
|
;;;; interleaving makes much of a difference, since we do not use
|
||||||
|
;;;; hardware rotate instructions in any case.
|
||||||
|
;;;;
|
||||||
|
|
||||||
|
#+cmu
|
||||||
|
(eval-when (:compile-toplevel)
|
||||||
|
(defparameter *old-expansion-limit* ext:*inline-expansion-limit*)
|
||||||
|
(setq ext:*inline-expansion-limit* (max ext:*inline-expansion-limit* 1000)))
|
||||||
|
|
||||||
|
#+sbcl
|
||||||
|
(eval-when (:compile-toplevel)
|
||||||
|
(defparameter *old-expansion-limit* sb-ext:*inline-expansion-limit*)
|
||||||
|
(setq sb-ext:*inline-expansion-limit* (max sb-ext:*inline-expansion-limit* 1000)))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Additional Keccak-f-1600 definitions
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(eval-when (:compile-toplevel :load-toplevel :execute)
|
||||||
|
(defconstant +keccak-state-splits+ 4
|
||||||
|
"Number of lane splits")
|
||||||
|
|
||||||
|
(defconstant +keccak-state-parts+ (* +keccak-state-lanes+ +keccak-state-splits+)
|
||||||
|
"Total number of partial lanes in Keccak state")
|
||||||
|
|
||||||
|
(defconstant +keccak-1600-part-width+ 16
|
||||||
|
"Partial lane width for Keccak-1600.")
|
||||||
|
|
||||||
|
(defconstant +keccak-1600-part-byte-width+ (truncate +keccak-1600-part-width+ 8)
|
||||||
|
"Partial lane width in bytes for Keccak-1600."))
|
||||||
|
|
||||||
|
(deftype keccak-1600-part ()
|
||||||
|
"Type of a partial keccak lane for Keccak-1600."
|
||||||
|
'fixnum)
|
||||||
|
|
||||||
|
(deftype keccak-1600-state ()
|
||||||
|
"Type of a keccak working state object for Keccak-1600."
|
||||||
|
`(simple-array keccak-1600-part
|
||||||
|
(,+keccak-state-parts+)))
|
||||||
|
|
||||||
|
(declaim (inline make-keccak-1600-state)
|
||||||
|
(ftype (function () keccak-1600-state) make-keccak-1600-state))
|
||||||
|
(defun make-keccak-1600-state ()
|
||||||
|
(declare #.*optimize-declaration*)
|
||||||
|
(make-array '(#.+keccak-state-parts+)
|
||||||
|
:element-type 'keccak-1600-part
|
||||||
|
:initial-element 0))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; De/Interleaving of bytes
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(eval-when (:compile-toplevel :load-toplevel :execute)
|
||||||
|
(defun make-interleave-table ()
|
||||||
|
(loop with result = (make-array 256 :element-type '(unsigned-byte 8))
|
||||||
|
for value from 0 to 255
|
||||||
|
for entry = 0
|
||||||
|
do
|
||||||
|
(loop for bit-index from 0 to 7
|
||||||
|
do
|
||||||
|
(setf (ldb (byte 1 (+ (truncate bit-index 4) (* 2 (mod bit-index 4))))
|
||||||
|
entry)
|
||||||
|
(ldb (byte 1 bit-index) value)))
|
||||||
|
(setf (aref result value) entry)
|
||||||
|
finally
|
||||||
|
(return result)))
|
||||||
|
|
||||||
|
(defun make-deinterleave-table ()
|
||||||
|
(loop with result = (make-array 256 :element-type '(unsigned-byte 8))
|
||||||
|
for value from 0 to 255
|
||||||
|
for entry = 0
|
||||||
|
do
|
||||||
|
(loop for bit-index from 0 to 7
|
||||||
|
do
|
||||||
|
(setf (ldb (byte 1 (+ (truncate bit-index 4) (* 2 (mod bit-index 4))))
|
||||||
|
entry)
|
||||||
|
(ldb (byte 1 bit-index) value)))
|
||||||
|
(setf (aref result entry) value)
|
||||||
|
finally
|
||||||
|
(return result))))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Transforming linear input/output to state array
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(defun keccak-state-merge-input (state bit-rate input start)
|
||||||
|
(declare (type keccak-1600-state state) (type (integer 0 1600) bit-rate)
|
||||||
|
(type (simple-array (unsigned-byte 8) (*)) input)
|
||||||
|
(type fixnum start)
|
||||||
|
#.*optimize-declaration*)
|
||||||
|
(let ((rate-bytes (truncate bit-rate 8))
|
||||||
|
(interleave-lookup (load-time-value (make-interleave-table) t)))
|
||||||
|
(declare (type (integer 0 200) rate-bytes)
|
||||||
|
(type (simple-array (unsigned-byte 8) (256)) interleave-lookup))
|
||||||
|
(dotimes (y #.+keccak-state-rows+)
|
||||||
|
(declare (fixnum y))
|
||||||
|
(dotimes (x #.+keccak-state-columns+)
|
||||||
|
(declare (fixnum x))
|
||||||
|
(let* ((element (+ (* y +keccak-state-columns+) x))
|
||||||
|
(part (* element +keccak-state-splits+))
|
||||||
|
(offset (* element +keccak-1600-lane-byte-width+))
|
||||||
|
(index (the fixnum (+ start offset))))
|
||||||
|
(declare (fixnum element part offset index))
|
||||||
|
(when (>= offset rate-bytes)
|
||||||
|
(return-from keccak-state-merge-input))
|
||||||
|
(setf (aref state part)
|
||||||
|
(logxor
|
||||||
|
(aref state part)
|
||||||
|
.
|
||||||
|
#.(loop for byte-index from 0
|
||||||
|
below +keccak-1600-lane-byte-width+
|
||||||
|
collect
|
||||||
|
`(the keccak-1600-part
|
||||||
|
(ash (ldb (byte 2 0)
|
||||||
|
(aref interleave-lookup
|
||||||
|
(aref input (+ index ,byte-index))))
|
||||||
|
,(* byte-index 2)))))
|
||||||
|
(aref state (1+ part))
|
||||||
|
(logxor
|
||||||
|
(aref state (1+ part))
|
||||||
|
.
|
||||||
|
#.(loop for byte-index from 0
|
||||||
|
below +keccak-1600-lane-byte-width+
|
||||||
|
collect
|
||||||
|
`(the keccak-1600-part
|
||||||
|
(ash (ldb (byte 2 2)
|
||||||
|
(aref interleave-lookup
|
||||||
|
(aref input (+ index ,byte-index))))
|
||||||
|
,(* byte-index 2)))))
|
||||||
|
(aref state (+ part 2))
|
||||||
|
(logxor
|
||||||
|
(aref state (+ part 2))
|
||||||
|
.
|
||||||
|
#.(loop for byte-index from 0
|
||||||
|
below +keccak-1600-lane-byte-width+
|
||||||
|
collect
|
||||||
|
`(the keccak-1600-part
|
||||||
|
(ash (ldb (byte 2 4)
|
||||||
|
(aref interleave-lookup
|
||||||
|
(aref input (+ index ,byte-index))))
|
||||||
|
,(* byte-index 2)))))
|
||||||
|
(aref state (+ part 3))
|
||||||
|
(logxor
|
||||||
|
(aref state (+ part 3))
|
||||||
|
.
|
||||||
|
#.(loop for byte-index from 0
|
||||||
|
below +keccak-1600-lane-byte-width+
|
||||||
|
collect
|
||||||
|
`(the keccak-1600-part
|
||||||
|
(ash (ldb (byte 2 6)
|
||||||
|
(aref interleave-lookup
|
||||||
|
(aref input (+ index ,byte-index))))
|
||||||
|
,(* byte-index 2)))))))))))
|
||||||
|
|
||||||
|
(defun keccak-state-extract-output (state output-bits)
|
||||||
|
(let* ((output-bytes (truncate output-bits 8))
|
||||||
|
(digest (make-array (list output-bytes) :element-type '(unsigned-byte 8)))
|
||||||
|
(deinterleave-lookup (load-time-value (make-deinterleave-table) t)))
|
||||||
|
(dotimes (x +keccak-state-columns+)
|
||||||
|
(dotimes (y +keccak-state-rows+)
|
||||||
|
(let* ((element (+ (* y +keccak-state-columns+) x))
|
||||||
|
(part (* element +keccak-state-splits+))
|
||||||
|
(offset (* element +keccak-1600-lane-byte-width+)))
|
||||||
|
(unless (>= offset output-bytes)
|
||||||
|
(loop with value-even = (aref state part)
|
||||||
|
with value-odd1 = (aref state (1+ part))
|
||||||
|
with value-odd2 = (aref state (+ part 2))
|
||||||
|
with value-odd3 = (aref state (+ part 3))
|
||||||
|
for index from offset
|
||||||
|
below (min (+ offset +keccak-1600-lane-byte-width+) output-bytes)
|
||||||
|
do
|
||||||
|
(setf (aref digest index)
|
||||||
|
(aref deinterleave-lookup
|
||||||
|
(dpb (ldb (byte 2 0) value-odd3) (byte 2 6)
|
||||||
|
(dpb (ldb (byte 2 0) value-odd2) (byte 2 4)
|
||||||
|
(dpb (ldb (byte 2 0) value-odd1) (byte 2 2)
|
||||||
|
(ldb (byte 2 0) value-even)))))
|
||||||
|
value-even (ash value-even -2)
|
||||||
|
value-odd1 (ash value-odd1 -2)
|
||||||
|
value-odd2 (ash value-odd2 -2)
|
||||||
|
value-odd3 (ash value-odd3 -2)))))))
|
||||||
|
digest))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Keccak Constants
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(declaim (inline keccak-f-round-constant)
|
||||||
|
(ftype (function ((integer 0 23) (integer 0 3)) keccak-1600-part)
|
||||||
|
keccak-f-round-constant))
|
||||||
|
(defun keccak-f-round-constant (i p)
|
||||||
|
(declare (type (integer 0 23) i) (type (integer 0 3) p)
|
||||||
|
#.*optimize-declaration*)
|
||||||
|
(let ((constants
|
||||||
|
(load-time-value
|
||||||
|
(make-array #.(* 24 +keccak-state-splits+)
|
||||||
|
:element-type 'keccak-1600-part
|
||||||
|
:initial-contents
|
||||||
|
(loop with itable = (make-interleave-table)
|
||||||
|
for rc across *keccak-f-round-constants*
|
||||||
|
nconc
|
||||||
|
(loop with even = 0
|
||||||
|
with odd1 = 0
|
||||||
|
with odd2 = 0
|
||||||
|
with odd3 = 0
|
||||||
|
for bit-offset from 0 below 64 by 8
|
||||||
|
for value = (aref itable
|
||||||
|
(ldb (byte 8 bit-offset) rc))
|
||||||
|
do
|
||||||
|
(setf (ldb (byte 2 (truncate bit-offset 4)) even)
|
||||||
|
(ldb (byte 2 0) value)
|
||||||
|
(ldb (byte 2 (truncate bit-offset 4)) odd1)
|
||||||
|
(ldb (byte 2 2) value)
|
||||||
|
(ldb (byte 2 (truncate bit-offset 4)) odd2)
|
||||||
|
(ldb (byte 2 4) value)
|
||||||
|
(ldb (byte 2 (truncate bit-offset 4)) odd3)
|
||||||
|
(ldb (byte 2 6) value))
|
||||||
|
finally
|
||||||
|
(return (list even odd1 odd2 odd3)))))
|
||||||
|
t)))
|
||||||
|
(declare (type (simple-array keccak-1600-part (#.(* 24 +keccak-state-splits+)))
|
||||||
|
constants))
|
||||||
|
(aref constants (+ (* i +keccak-state-splits+) p))))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Helper: Rotation
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(declaim (inline keccak-f-rot-part)
|
||||||
|
(ftype (function (keccak-1600-part (integer 0 16)) keccak-1600-part)
|
||||||
|
keccak-f-rot-part))
|
||||||
|
(defun keccak-f-rot-part (value offset)
|
||||||
|
(declare (type (integer 0 16) offset)
|
||||||
|
(type keccak-1600-part value)
|
||||||
|
#.*optimize-declaration*
|
||||||
|
#+sbcl
|
||||||
|
(sb-ext:muffle-conditions sb-ext:code-deletion-note))
|
||||||
|
#+(and xxx sbcl)
|
||||||
|
(if (or (zerop offset) (= offset 16))
|
||||||
|
value
|
||||||
|
(sb-rotate-byte:rotate-byte offset (byte 16 0) value))
|
||||||
|
#-(and xxx sbcl)
|
||||||
|
(if (or (zerop offset) (= offset 16))
|
||||||
|
value
|
||||||
|
(logior (the keccak-1600-part (ash (ldb (byte (- 16 offset) 0) value) offset))
|
||||||
|
(ash value (- offset 16)))))
|
||||||
|
|
||||||
|
(declaim (inline keccak-f-rot)
|
||||||
|
(ftype (function (keccak-1600-part keccak-1600-part
|
||||||
|
keccak-1600-part keccak-1600-part (integer 0 63))
|
||||||
|
(values keccak-1600-part keccak-1600-part
|
||||||
|
keccak-1600-part keccak-1600-part))
|
||||||
|
keccak-f-rot))
|
||||||
|
(defun keccak-f-rot (value-even value-odd1 value-odd2 value-odd3 offset)
|
||||||
|
(declare (type (integer 0 63) offset)
|
||||||
|
(type keccak-1600-part value-even value-odd1 value-odd2 value-odd3)
|
||||||
|
#.*optimize-declaration*
|
||||||
|
#+sbcl
|
||||||
|
(sb-ext:muffle-conditions sb-ext:code-deletion-note))
|
||||||
|
(case (mod offset 4)
|
||||||
|
(0
|
||||||
|
(values
|
||||||
|
(keccak-f-rot-part value-even (truncate offset 4))
|
||||||
|
(keccak-f-rot-part value-odd1 (truncate offset 4))
|
||||||
|
(keccak-f-rot-part value-odd2 (truncate offset 4))
|
||||||
|
(keccak-f-rot-part value-odd3 (truncate offset 4))))
|
||||||
|
(1
|
||||||
|
(values
|
||||||
|
(keccak-f-rot-part value-odd3 (1+ (truncate offset 4)))
|
||||||
|
(keccak-f-rot-part value-even (truncate offset 4))
|
||||||
|
(keccak-f-rot-part value-odd1 (truncate offset 4))
|
||||||
|
(keccak-f-rot-part value-odd2 (truncate offset 4))))
|
||||||
|
(2
|
||||||
|
(values
|
||||||
|
(keccak-f-rot-part value-odd2 (1+ (truncate offset 4)))
|
||||||
|
(keccak-f-rot-part value-odd3 (1+ (truncate offset 4)))
|
||||||
|
(keccak-f-rot-part value-even (truncate offset 4))
|
||||||
|
(keccak-f-rot-part value-odd1 (truncate offset 4))))
|
||||||
|
(3
|
||||||
|
(values
|
||||||
|
(keccak-f-rot-part value-odd1 (1+ (truncate offset 4)))
|
||||||
|
(keccak-f-rot-part value-odd2 (1+ (truncate offset 4)))
|
||||||
|
(keccak-f-rot-part value-odd3 (1+ (truncate offset 4)))
|
||||||
|
(keccak-f-rot-part value-even (truncate offset 4))))))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; State and Temporary Variable Accessors
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(defmacro with-state-accessors ((&rest states) &body body)
|
||||||
|
"Bind the contents of the state(s) array(s) to local variables, and save
|
||||||
|
the content on normal form exit."
|
||||||
|
(let ((bindings nil) (mappings nil) (save-forms nil))
|
||||||
|
(loop for state in states
|
||||||
|
for map = (make-array '(#.+keccak-state-columns+ #.+keccak-state-rows+
|
||||||
|
#.+keccak-state-splits+))
|
||||||
|
do
|
||||||
|
(dotimes (y +keccak-state-rows+)
|
||||||
|
(dotimes (x +keccak-state-columns+)
|
||||||
|
(dotimes (p +keccak-state-splits+)
|
||||||
|
(let ((sym (make-symbol (format nil "~A-~D-~D-~D" state x y p))))
|
||||||
|
(setf (aref map x y p) sym)
|
||||||
|
(push `(,sym (aref ,state ,(+ p (* (+ x (* y +keccak-state-columns+))
|
||||||
|
+keccak-state-splits+))))
|
||||||
|
bindings)
|
||||||
|
(push `(setf (aref ,state ,(+ p (* (+ x (* y +keccak-state-columns+))
|
||||||
|
+keccak-state-splits+)))
|
||||||
|
,sym)
|
||||||
|
save-forms)))))
|
||||||
|
(push (cons state map) mappings))
|
||||||
|
`(let (,@bindings)
|
||||||
|
(declare (ignorable ,@(mapcar #'car bindings))
|
||||||
|
(type keccak-1600-part ,@(mapcar #'car bindings)))
|
||||||
|
(macrolet ((state-aref (state x y p &environment env)
|
||||||
|
(let ((entry (assoc state ',mappings)))
|
||||||
|
(unless entry (error "Strange: ~S!" state))
|
||||||
|
(aref (cdr entry)
|
||||||
|
(eval (trivial-macroexpand-all x env))
|
||||||
|
(eval (trivial-macroexpand-all y env))
|
||||||
|
(eval (trivial-macroexpand-all p env))))))
|
||||||
|
(multiple-value-prog1 (progn ,@body)
|
||||||
|
,@save-forms)))))
|
||||||
|
|
||||||
|
(defmacro with-temp-state ((&rest temps) &body body)
|
||||||
|
"Bind local variables for each temporary state."
|
||||||
|
(let ((bindings nil) (mappings nil))
|
||||||
|
(loop for temp in temps
|
||||||
|
for map = (make-array '(#.+keccak-state-columns+ #.+keccak-state-rows+
|
||||||
|
#.+keccak-state-splits+))
|
||||||
|
do
|
||||||
|
(dotimes (y +keccak-state-rows+)
|
||||||
|
(dotimes (x +keccak-state-columns+)
|
||||||
|
(dotimes (p +keccak-state-splits+)
|
||||||
|
(let ((sym (make-symbol (format nil "~A-~D-~D-~D" temp x y p))))
|
||||||
|
(setf (aref map x y p) sym)
|
||||||
|
(push `(,sym 0) bindings)))))
|
||||||
|
(push (cons temp map) mappings))
|
||||||
|
`(let (,@bindings)
|
||||||
|
(declare (ignorable ,@(mapcar #'car bindings))
|
||||||
|
(type keccak-1600-part ,@(mapcar #'car bindings)))
|
||||||
|
(macrolet ((temp-state-aref (temp x y p &environment env)
|
||||||
|
(let ((entry (assoc temp ',mappings)))
|
||||||
|
(unless entry (error "Strange: ~S!" temp))
|
||||||
|
(aref (cdr entry)
|
||||||
|
(eval (trivial-macroexpand-all x env))
|
||||||
|
(eval (trivial-macroexpand-all y env))
|
||||||
|
(eval (trivial-macroexpand-all p env))))))
|
||||||
|
,@body))))
|
||||||
|
|
||||||
|
(defmacro with-temp-rows ((&rest rows) &body body)
|
||||||
|
"Bind local variables for each temporary row."
|
||||||
|
(let ((bindings nil) (mappings nil))
|
||||||
|
(loop for row in rows
|
||||||
|
for map = (make-array '(#.+keccak-state-columns+ #.+keccak-state-splits+))
|
||||||
|
do
|
||||||
|
(dotimes (x +keccak-state-columns+)
|
||||||
|
(dotimes (p +keccak-state-splits+)
|
||||||
|
(let ((sym (make-symbol (format nil "~A-~D-~D" row x p))))
|
||||||
|
(setf (aref map x p) sym)
|
||||||
|
(push `(,sym 0) bindings))))
|
||||||
|
(push (cons row map) mappings))
|
||||||
|
`(let (,@bindings)
|
||||||
|
(declare (ignorable ,@(mapcar #'car bindings))
|
||||||
|
(type keccak-1600-part ,@(mapcar #'car bindings)))
|
||||||
|
(macrolet ((temp-row-aref (row x p &environment env)
|
||||||
|
(let ((entry (assoc row ',mappings)))
|
||||||
|
(unless entry (error "Strange: ~S!" row))
|
||||||
|
(aref (cdr entry)
|
||||||
|
(eval (trivial-macroexpand-all x env))
|
||||||
|
(eval (trivial-macroexpand-all p env))))))
|
||||||
|
,@body))))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Keccak-f permutation
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(declaim (ftype (function (keccak-1600-state) keccak-1600-state) keccak-f))
|
||||||
|
(defun keccak-f (a)
|
||||||
|
(declare (type keccak-1600-state a)
|
||||||
|
#.*optimize-declaration*)
|
||||||
|
(with-state-accessors (a)
|
||||||
|
(with-temp-state (b)
|
||||||
|
(with-temp-rows (c d)
|
||||||
|
(dotimes (i #.(+ 12 (* 2 (truncate (log +keccak-1600-lane-width+ 2)))))
|
||||||
|
(dotimes-unrolled (x +keccak-state-columns+)
|
||||||
|
(dotimes-unrolled (p +keccak-state-splits+)
|
||||||
|
(setf (temp-row-aref c x p)
|
||||||
|
(logxor (state-aref a x 0 p)
|
||||||
|
(state-aref a x 1 p)
|
||||||
|
(state-aref a x 2 p)
|
||||||
|
(state-aref a x 3 p)
|
||||||
|
(state-aref a x 4 p)))))
|
||||||
|
(dotimes-unrolled (x +keccak-state-columns+)
|
||||||
|
(setf (temp-row-aref d x 0)
|
||||||
|
(logxor (temp-row-aref c (mod (+ +keccak-state-columns+ (1- x))
|
||||||
|
+keccak-state-columns+)
|
||||||
|
0)
|
||||||
|
(keccak-f-rot-part
|
||||||
|
(temp-row-aref c (mod (1+ x) +keccak-state-columns+) 3)
|
||||||
|
1))
|
||||||
|
(temp-row-aref d x 1)
|
||||||
|
(logxor (temp-row-aref c (mod (+ +keccak-state-columns+ (1- x))
|
||||||
|
+keccak-state-columns+)
|
||||||
|
1)
|
||||||
|
(temp-row-aref c (mod (1+ x) +keccak-state-columns+) 0))
|
||||||
|
(temp-row-aref d x 2)
|
||||||
|
(logxor (temp-row-aref c (mod (+ +keccak-state-columns+ (1- x))
|
||||||
|
+keccak-state-columns+)
|
||||||
|
2)
|
||||||
|
(temp-row-aref c (mod (1+ x) +keccak-state-columns+) 1))
|
||||||
|
(temp-row-aref d x 3)
|
||||||
|
(logxor (temp-row-aref c (mod (+ +keccak-state-columns+ (1- x))
|
||||||
|
+keccak-state-columns+)
|
||||||
|
3)
|
||||||
|
(temp-row-aref c (mod (1+ x) +keccak-state-columns+) 2))))
|
||||||
|
(dotimes-unrolled (x +keccak-state-columns+)
|
||||||
|
(dotimes-unrolled (y +keccak-state-rows+)
|
||||||
|
(dotimes-unrolled (p +keccak-state-splits+)
|
||||||
|
(setf (state-aref a x y p)
|
||||||
|
(logxor (state-aref a x y p) (temp-row-aref d x p))))))
|
||||||
|
(dotimes-unrolled (x +keccak-state-columns+)
|
||||||
|
(dotimes-unrolled (y +keccak-state-rows+)
|
||||||
|
(setf (values
|
||||||
|
(temp-state-aref b y
|
||||||
|
(mod (+ (* 2 x) (* 3 y)) +keccak-state-rows+)
|
||||||
|
0)
|
||||||
|
(temp-state-aref b y
|
||||||
|
(mod (+ (* 2 x) (* 3 y)) +keccak-state-rows+)
|
||||||
|
1)
|
||||||
|
(temp-state-aref b y
|
||||||
|
(mod (+ (* 2 x) (* 3 y)) +keccak-state-rows+)
|
||||||
|
2)
|
||||||
|
(temp-state-aref b y
|
||||||
|
(mod (+ (* 2 x) (* 3 y)) +keccak-state-rows+)
|
||||||
|
3))
|
||||||
|
(keccak-f-rot (state-aref a x y 0) (state-aref a x y 1)
|
||||||
|
(state-aref a x y 2) (state-aref a x y 3)
|
||||||
|
(get-rotate-offset x y)))))
|
||||||
|
(dotimes-unrolled (x +keccak-state-columns+)
|
||||||
|
(dotimes-unrolled (y +keccak-state-rows+)
|
||||||
|
(dotimes-unrolled (p +keccak-state-splits+)
|
||||||
|
(setf (state-aref a x y p)
|
||||||
|
(logxor (temp-state-aref b x y p)
|
||||||
|
(logandc1
|
||||||
|
(temp-state-aref b (mod (1+ x) +keccak-state-columns+)
|
||||||
|
y p)
|
||||||
|
(temp-state-aref b (mod (+ x 2) +keccak-state-columns+)
|
||||||
|
y p)))))))
|
||||||
|
(dotimes-unrolled (p +keccak-state-splits+)
|
||||||
|
(setf (state-aref a 0 0 p)
|
||||||
|
(logxor (state-aref a 0 0 p)
|
||||||
|
(keccak-f-round-constant i p)))))))
|
||||||
|
a))
|
||||||
|
|
||||||
|
#+cmu
|
||||||
|
(eval-when (:compile-toplevel)
|
||||||
|
(setq ext:*inline-expansion-limit* *old-expansion-limit*))
|
||||||
|
|
||||||
|
#+sbcl
|
||||||
|
(eval-when (:compile-toplevel)
|
||||||
|
(setq sb-ext:*inline-expansion-limit* *old-expansion-limit*))
|
||||||
431
keccak-32bit.lisp
Normal file
431
keccak-32bit.lisp
Normal file
@ -0,0 +1,431 @@
|
|||||||
|
;;;; SHA3 --- Secure Hash Algorithm 3 (Keccak) Implementation
|
||||||
|
;;;;
|
||||||
|
;;;; Copyright (C) 2012 PMSF IT Consulting Pierre R. Mai.
|
||||||
|
;;;;
|
||||||
|
;;;; Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
;;;; a copy of this software and associated documentation files (the
|
||||||
|
;;;; "Software"), to deal in the Software without restriction, including
|
||||||
|
;;;; without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
;;;; distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
;;;; permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
;;;; the following conditions:
|
||||||
|
;;;;
|
||||||
|
;;;; The above copyright notice and this permission notice shall be
|
||||||
|
;;;; included in all copies or substantial portions of the Software.
|
||||||
|
;;;;
|
||||||
|
;;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
;;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
;;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
;;;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
;;;; OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
;;;; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
;;;; OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
;;;;
|
||||||
|
;;;; Except as contained in this notice, the name of the author shall
|
||||||
|
;;;; not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
;;;; other dealings in this Software without prior written authorization
|
||||||
|
;;;; from the author.
|
||||||
|
;;;;
|
||||||
|
;;;; $Id$
|
||||||
|
|
||||||
|
(cl:in-package #:sha3)
|
||||||
|
|
||||||
|
;;;; %File Description:
|
||||||
|
;;;;
|
||||||
|
;;;; This file contains an implementation of Keccak 1600 tuned to
|
||||||
|
;;;; implementations that support unboxed arithmetic on (unsigned-byte
|
||||||
|
;;;; 32).
|
||||||
|
;;;;
|
||||||
|
;;;; Implementation Choices:
|
||||||
|
;;;;
|
||||||
|
;;;; This is a fairly straightforward implementation of Keccak 1600,
|
||||||
|
;;;; mostly tuned to sbcl for x86, and to some degree to cmucl for
|
||||||
|
;;;; x86. It employs a bit of loop unrolling at compile-time, and
|
||||||
|
;;;; splits the 64bit Keccak 1600 lanes into two 32 bit words with bit
|
||||||
|
;;;; interleaving, thereby being able to still use hardware rotate
|
||||||
|
;;;; support where possible. More advanced implementation techniques
|
||||||
|
;;;; like plane-per-plane processing, lane complementing and early
|
||||||
|
;;;; parity (c.f. the Keccak implementation overview) did not seem to
|
||||||
|
;;;; work well with SBCL's compiler, resulting in slower, less
|
||||||
|
;;;; readable code.
|
||||||
|
;;;;
|
||||||
|
|
||||||
|
#+cmu
|
||||||
|
(eval-when (:compile-toplevel)
|
||||||
|
(defparameter *old-expansion-limit* ext:*inline-expansion-limit*)
|
||||||
|
(setq ext:*inline-expansion-limit* (max ext:*inline-expansion-limit* 1000)))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Additional Keccak-f-1600 definitions
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(eval-when (:compile-toplevel :load-toplevel :execute)
|
||||||
|
(defconstant +keccak-state-splits+ 2
|
||||||
|
"Number of lane splits")
|
||||||
|
|
||||||
|
(defconstant +keccak-state-parts+ (* +keccak-state-lanes+ +keccak-state-splits+)
|
||||||
|
"Total number of partial lanes in Keccak state")
|
||||||
|
|
||||||
|
(defconstant +keccak-1600-part-width+ 32
|
||||||
|
"Partial lane width for Keccak-1600.")
|
||||||
|
|
||||||
|
(defconstant +keccak-1600-part-byte-width+ (truncate +keccak-1600-part-width+ 8)
|
||||||
|
"Partial lane width in bytes for Keccak-1600."))
|
||||||
|
|
||||||
|
(deftype keccak-1600-part ()
|
||||||
|
"Type of a partial keccak lane for Keccak-1600."
|
||||||
|
`(unsigned-byte ,+keccak-1600-part-width+))
|
||||||
|
|
||||||
|
(deftype keccak-1600-state ()
|
||||||
|
"Type of a keccak working state object for Keccak-1600."
|
||||||
|
`(simple-array keccak-1600-part
|
||||||
|
(,+keccak-state-parts+)))
|
||||||
|
|
||||||
|
(declaim (inline make-keccak-1600-state)
|
||||||
|
(ftype (function () keccak-1600-state) make-keccak-1600-state))
|
||||||
|
(defun make-keccak-1600-state ()
|
||||||
|
(declare #.*optimize-declaration*)
|
||||||
|
(make-array '(#.+keccak-state-parts+)
|
||||||
|
:element-type 'keccak-1600-part
|
||||||
|
:initial-element 0))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; De/Interleaving of bytes
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(eval-when (:compile-toplevel :load-toplevel :execute)
|
||||||
|
(defun make-interleave-table ()
|
||||||
|
(loop with result = (make-array 256 :element-type '(unsigned-byte 8))
|
||||||
|
for value from 0 to 255
|
||||||
|
for entry = 0
|
||||||
|
do
|
||||||
|
(loop for bit-index from 0 to 7
|
||||||
|
do
|
||||||
|
(setf (ldb (byte 1 (+ (truncate bit-index 2) (* 4 (mod bit-index 2))))
|
||||||
|
entry)
|
||||||
|
(ldb (byte 1 bit-index) value)))
|
||||||
|
(setf (aref result value) entry)
|
||||||
|
finally
|
||||||
|
(return result)))
|
||||||
|
|
||||||
|
(defun make-deinterleave-table ()
|
||||||
|
(loop with result = (make-array 256 :element-type '(unsigned-byte 8))
|
||||||
|
for value from 0 to 255
|
||||||
|
for entry = 0
|
||||||
|
do
|
||||||
|
(loop for bit-index from 0 to 7
|
||||||
|
do
|
||||||
|
(setf (ldb (byte 1 (+ (truncate bit-index 2) (* 4 (mod bit-index 2))))
|
||||||
|
entry)
|
||||||
|
(ldb (byte 1 bit-index) value)))
|
||||||
|
(setf (aref result entry) value)
|
||||||
|
finally
|
||||||
|
(return result))))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Transforming linear input/output to state array
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(defun keccak-state-merge-input (state bit-rate input start)
|
||||||
|
(declare (type keccak-1600-state state) (type (integer 0 1600) bit-rate)
|
||||||
|
(type (simple-array (unsigned-byte 8) (*)) input)
|
||||||
|
(type fixnum start)
|
||||||
|
#.*optimize-declaration*)
|
||||||
|
(let ((rate-bytes (truncate bit-rate 8))
|
||||||
|
(interleave-lookup (load-time-value (make-interleave-table) t)))
|
||||||
|
(declare (type (integer 0 200) rate-bytes)
|
||||||
|
(type (simple-array (unsigned-byte 8) (256)) interleave-lookup))
|
||||||
|
(dotimes (y #.+keccak-state-rows+)
|
||||||
|
(declare (fixnum y))
|
||||||
|
(dotimes (x #.+keccak-state-columns+)
|
||||||
|
(declare (fixnum x))
|
||||||
|
(let* ((element (+ (* y +keccak-state-columns+) x))
|
||||||
|
(part (* element +keccak-state-splits+))
|
||||||
|
(offset (* element +keccak-1600-lane-byte-width+))
|
||||||
|
(index (the fixnum (+ start offset))))
|
||||||
|
(declare (fixnum element part offset index))
|
||||||
|
(when (>= offset rate-bytes)
|
||||||
|
(return-from keccak-state-merge-input))
|
||||||
|
(setf (aref state part)
|
||||||
|
(logxor
|
||||||
|
(aref state part)
|
||||||
|
.
|
||||||
|
#.(loop for byte-index from 0
|
||||||
|
below +keccak-1600-lane-byte-width+
|
||||||
|
collect
|
||||||
|
`(the keccak-1600-part
|
||||||
|
(ash (ldb (byte 4 0)
|
||||||
|
(aref interleave-lookup
|
||||||
|
(aref input (+ index ,byte-index))))
|
||||||
|
,(* byte-index 4)))))
|
||||||
|
(aref state (1+ part))
|
||||||
|
(logxor
|
||||||
|
(aref state (1+ part))
|
||||||
|
.
|
||||||
|
#.(loop for byte-index from 0
|
||||||
|
below +keccak-1600-lane-byte-width+
|
||||||
|
collect
|
||||||
|
`(the keccak-1600-part
|
||||||
|
(ash (ldb (byte 4 4)
|
||||||
|
(aref interleave-lookup
|
||||||
|
(aref input (+ index ,byte-index))))
|
||||||
|
,(* byte-index 4)))))))))))
|
||||||
|
|
||||||
|
(defun keccak-state-extract-output (state output-bits)
|
||||||
|
(let* ((output-bytes (truncate output-bits 8))
|
||||||
|
(digest (make-array (list output-bytes) :element-type '(unsigned-byte 8)))
|
||||||
|
(deinterleave-lookup (load-time-value (make-deinterleave-table) t)))
|
||||||
|
(dotimes (x +keccak-state-columns+)
|
||||||
|
(dotimes (y +keccak-state-rows+)
|
||||||
|
(let* ((element (+ (* y +keccak-state-columns+) x))
|
||||||
|
(part (* element +keccak-state-splits+))
|
||||||
|
(offset (* element +keccak-1600-lane-byte-width+)))
|
||||||
|
(unless (>= offset output-bytes)
|
||||||
|
(loop with value-even = (aref state part)
|
||||||
|
with value-odd = (aref state (1+ part))
|
||||||
|
for index from offset
|
||||||
|
below (min (+ offset +keccak-1600-lane-byte-width+) output-bytes)
|
||||||
|
do
|
||||||
|
(setf (aref digest index)
|
||||||
|
(aref deinterleave-lookup (dpb (ldb (byte 4 0) value-odd)
|
||||||
|
(byte 4 4)
|
||||||
|
(ldb (byte 4 0) value-even)))
|
||||||
|
value-even (ash value-even -4)
|
||||||
|
value-odd (ash value-odd -4)))))))
|
||||||
|
digest))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Keccak Constants
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(declaim (inline keccak-f-round-constant)
|
||||||
|
(ftype (function ((integer 0 23) (integer 0 1)) keccak-1600-part)
|
||||||
|
keccak-f-round-constant))
|
||||||
|
(defun keccak-f-round-constant (i p)
|
||||||
|
(declare (type (integer 0 23) i) (type (integer 0 1) p)
|
||||||
|
#.*optimize-declaration*)
|
||||||
|
(let ((constants
|
||||||
|
(load-time-value
|
||||||
|
(make-array #.(* 24 +keccak-state-splits+)
|
||||||
|
:element-type 'keccak-1600-part
|
||||||
|
:initial-contents
|
||||||
|
(loop with itable = (make-interleave-table)
|
||||||
|
for rc across *keccak-f-round-constants*
|
||||||
|
nconc
|
||||||
|
(loop with even = 0
|
||||||
|
with odd = 0
|
||||||
|
for bit-offset from 0 below 64 by 8
|
||||||
|
for value = (aref itable
|
||||||
|
(ldb (byte 8 bit-offset) rc))
|
||||||
|
do
|
||||||
|
(setf (ldb (byte 4 (truncate bit-offset 2)) even)
|
||||||
|
(ldb (byte 4 0) value)
|
||||||
|
(ldb (byte 4 (truncate bit-offset 2)) odd)
|
||||||
|
(ldb (byte 4 4) value))
|
||||||
|
finally
|
||||||
|
(return (list even odd)))))
|
||||||
|
t)))
|
||||||
|
(declare (type (simple-array keccak-1600-part (#.(* 24 +keccak-state-splits+)))
|
||||||
|
constants))
|
||||||
|
(aref constants (+ (* i +keccak-state-splits+) p))))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Helper: Rotation
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(declaim (inline keccak-f-rot-part)
|
||||||
|
(ftype (function (keccak-1600-part (integer 0 32)) keccak-1600-part)
|
||||||
|
keccak-f-rot-part))
|
||||||
|
(defun keccak-f-rot-part (value offset)
|
||||||
|
(declare (type (integer 0 32) offset)
|
||||||
|
(type keccak-1600-part value)
|
||||||
|
#.*optimize-declaration*
|
||||||
|
#+sbcl
|
||||||
|
(sb-ext:muffle-conditions sb-ext:code-deletion-note))
|
||||||
|
#+sbcl
|
||||||
|
(if (or (zerop offset) (= offset 32))
|
||||||
|
value
|
||||||
|
(sb-rotate-byte:rotate-byte offset (byte 32 0) value))
|
||||||
|
#-sbcl
|
||||||
|
(if (or (zerop offset) (= offset 32))
|
||||||
|
value
|
||||||
|
#+ccl
|
||||||
|
(logior (the keccak-1600-part (ash (ldb (byte (- 32 offset) 0) value) offset))
|
||||||
|
(ash value (- offset 32)))
|
||||||
|
#-ccl
|
||||||
|
(logior (ldb (byte 32 0) (ash value offset))
|
||||||
|
(ash value (- offset 32)))))
|
||||||
|
|
||||||
|
(declaim (inline keccak-f-rot)
|
||||||
|
(ftype (function (keccak-1600-part keccak-1600-part (integer 0 63))
|
||||||
|
(values keccak-1600-part keccak-1600-part))
|
||||||
|
keccak-f-rot))
|
||||||
|
(defun keccak-f-rot (value-even value-odd offset)
|
||||||
|
(declare (type (integer 0 63) offset)
|
||||||
|
(type keccak-1600-part value-even value-odd)
|
||||||
|
#.*optimize-declaration*
|
||||||
|
#+sbcl
|
||||||
|
(sb-ext:muffle-conditions sb-ext:code-deletion-note))
|
||||||
|
(if (evenp offset)
|
||||||
|
(values
|
||||||
|
(keccak-f-rot-part value-even (truncate offset 2))
|
||||||
|
(keccak-f-rot-part value-odd (truncate offset 2)))
|
||||||
|
(values
|
||||||
|
(keccak-f-rot-part value-odd (1+ (truncate offset 2)))
|
||||||
|
(keccak-f-rot-part value-even (truncate offset 2)))))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; State and Temporary Variable Accessors
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(defmacro with-state-accessors ((&rest states) &body body)
|
||||||
|
"Bind the contents of the state(s) array(s) to local variables, and save
|
||||||
|
the content on normal form exit."
|
||||||
|
(let ((bindings nil) (mappings nil) (save-forms nil))
|
||||||
|
(loop for state in states
|
||||||
|
for map = (make-array '(#.+keccak-state-columns+ #.+keccak-state-rows+
|
||||||
|
#.+keccak-state-splits+))
|
||||||
|
do
|
||||||
|
(dotimes (y +keccak-state-rows+)
|
||||||
|
(dotimes (x +keccak-state-columns+)
|
||||||
|
(dotimes (p +keccak-state-splits+)
|
||||||
|
(let ((sym (make-symbol (format nil "~A-~D-~D-~D" state x y p))))
|
||||||
|
(setf (aref map x y p) sym)
|
||||||
|
(push `(,sym (aref ,state ,(+ p (* (+ x (* y +keccak-state-columns+))
|
||||||
|
+keccak-state-splits+))))
|
||||||
|
bindings)
|
||||||
|
(push `(setf (aref ,state ,(+ p (* (+ x (* y +keccak-state-columns+))
|
||||||
|
+keccak-state-splits+)))
|
||||||
|
,sym)
|
||||||
|
save-forms)))))
|
||||||
|
(push (cons state map) mappings))
|
||||||
|
`(let (,@bindings)
|
||||||
|
(declare (ignorable ,@(mapcar #'car bindings))
|
||||||
|
(type keccak-1600-part ,@(mapcar #'car bindings)))
|
||||||
|
(macrolet ((state-aref (state x y p &environment env)
|
||||||
|
(let ((entry (assoc state ',mappings)))
|
||||||
|
(unless entry (error "Strange: ~S!" state))
|
||||||
|
(aref (cdr entry)
|
||||||
|
(eval (trivial-macroexpand-all x env))
|
||||||
|
(eval (trivial-macroexpand-all y env))
|
||||||
|
(eval (trivial-macroexpand-all p env))))))
|
||||||
|
(multiple-value-prog1 (progn ,@body)
|
||||||
|
,@save-forms)))))
|
||||||
|
|
||||||
|
(defmacro with-temp-state ((&rest temps) &body body)
|
||||||
|
"Bind local variables for each temporary state."
|
||||||
|
(let ((bindings nil) (mappings nil))
|
||||||
|
(loop for temp in temps
|
||||||
|
for map = (make-array '(#.+keccak-state-columns+ #.+keccak-state-rows+
|
||||||
|
#.+keccak-state-splits+))
|
||||||
|
do
|
||||||
|
(dotimes (y +keccak-state-rows+)
|
||||||
|
(dotimes (x +keccak-state-columns+)
|
||||||
|
(dotimes (p +keccak-state-splits+)
|
||||||
|
(let ((sym (make-symbol (format nil "~A-~D-~D-~D" temp x y p))))
|
||||||
|
(setf (aref map x y p) sym)
|
||||||
|
(push `(,sym 0) bindings)))))
|
||||||
|
(push (cons temp map) mappings))
|
||||||
|
`(let (,@bindings)
|
||||||
|
(declare (ignorable ,@(mapcar #'car bindings))
|
||||||
|
(type keccak-1600-part ,@(mapcar #'car bindings)))
|
||||||
|
(macrolet ((temp-state-aref (temp x y p &environment env)
|
||||||
|
(let ((entry (assoc temp ',mappings)))
|
||||||
|
(unless entry (error "Strange: ~S!" temp))
|
||||||
|
(aref (cdr entry)
|
||||||
|
(eval (trivial-macroexpand-all x env))
|
||||||
|
(eval (trivial-macroexpand-all y env))
|
||||||
|
(eval (trivial-macroexpand-all p env))))))
|
||||||
|
,@body))))
|
||||||
|
|
||||||
|
(defmacro with-temp-rows ((&rest rows) &body body)
|
||||||
|
"Bind local variables for each temporary row."
|
||||||
|
(let ((bindings nil) (mappings nil))
|
||||||
|
(loop for row in rows
|
||||||
|
for map = (make-array '(#.+keccak-state-columns+ #.+keccak-state-splits+))
|
||||||
|
do
|
||||||
|
(dotimes (x +keccak-state-columns+)
|
||||||
|
(dotimes (p +keccak-state-splits+)
|
||||||
|
(let ((sym (make-symbol (format nil "~A-~D-~D" row x p))))
|
||||||
|
(setf (aref map x p) sym)
|
||||||
|
(push `(,sym 0) bindings))))
|
||||||
|
(push (cons row map) mappings))
|
||||||
|
`(let (,@bindings)
|
||||||
|
(declare (ignorable ,@(mapcar #'car bindings))
|
||||||
|
(type keccak-1600-part ,@(mapcar #'car bindings)))
|
||||||
|
(macrolet ((temp-row-aref (row x p &environment env)
|
||||||
|
(let ((entry (assoc row ',mappings)))
|
||||||
|
(unless entry (error "Strange: ~S!" row))
|
||||||
|
(aref (cdr entry)
|
||||||
|
(eval (trivial-macroexpand-all x env))
|
||||||
|
(eval (trivial-macroexpand-all p env))))))
|
||||||
|
,@body))))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Keccak-f permutation
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(declaim (ftype (function (keccak-1600-state) keccak-1600-state) keccak-f))
|
||||||
|
(defun keccak-f (a)
|
||||||
|
(declare (type keccak-1600-state a)
|
||||||
|
#.*optimize-declaration*)
|
||||||
|
(with-state-accessors (a)
|
||||||
|
(with-temp-state (b)
|
||||||
|
(with-temp-rows (c d)
|
||||||
|
(dotimes (i #.(+ 12 (* 2 (truncate (log +keccak-1600-lane-width+ 2)))))
|
||||||
|
(dotimes-unrolled (x +keccak-state-columns+)
|
||||||
|
(dotimes-unrolled (p +keccak-state-splits+)
|
||||||
|
(setf (temp-row-aref c x p)
|
||||||
|
(logxor (state-aref a x 0 p)
|
||||||
|
(state-aref a x 1 p)
|
||||||
|
(state-aref a x 2 p)
|
||||||
|
(state-aref a x 3 p)
|
||||||
|
(state-aref a x 4 p)))))
|
||||||
|
(dotimes-unrolled (x +keccak-state-columns+)
|
||||||
|
(setf (temp-row-aref d x 0)
|
||||||
|
(logxor (temp-row-aref c (mod (+ +keccak-state-columns+ (1- x))
|
||||||
|
+keccak-state-columns+)
|
||||||
|
0)
|
||||||
|
(keccak-f-rot-part
|
||||||
|
(temp-row-aref c (mod (1+ x) +keccak-state-columns+) 1)
|
||||||
|
1))
|
||||||
|
(temp-row-aref d x 1)
|
||||||
|
(logxor (temp-row-aref c (mod (+ +keccak-state-columns+ (1- x))
|
||||||
|
+keccak-state-columns+)
|
||||||
|
1)
|
||||||
|
(temp-row-aref c (mod (1+ x) +keccak-state-columns+) 0))))
|
||||||
|
(dotimes-unrolled (x +keccak-state-columns+)
|
||||||
|
(dotimes-unrolled (y +keccak-state-rows+)
|
||||||
|
(dotimes-unrolled (p +keccak-state-splits+)
|
||||||
|
(setf (state-aref a x y p)
|
||||||
|
(logxor (state-aref a x y p) (temp-row-aref d x p))))))
|
||||||
|
(dotimes-unrolled (x +keccak-state-columns+)
|
||||||
|
(dotimes-unrolled (y +keccak-state-rows+)
|
||||||
|
(setf (values
|
||||||
|
(temp-state-aref b y
|
||||||
|
(mod (+ (* 2 x) (* 3 y)) +keccak-state-rows+)
|
||||||
|
0)
|
||||||
|
(temp-state-aref b y
|
||||||
|
(mod (+ (* 2 x) (* 3 y)) +keccak-state-rows+)
|
||||||
|
1))
|
||||||
|
(keccak-f-rot (state-aref a x y 0) (state-aref a x y 1)
|
||||||
|
(get-rotate-offset x y)))))
|
||||||
|
(dotimes-unrolled (x +keccak-state-columns+)
|
||||||
|
(dotimes-unrolled (y +keccak-state-rows+)
|
||||||
|
(dotimes-unrolled (p +keccak-state-splits+)
|
||||||
|
(setf (state-aref a x y p)
|
||||||
|
(logxor (temp-state-aref b x y p)
|
||||||
|
(logandc1
|
||||||
|
(temp-state-aref b (mod (1+ x) +keccak-state-columns+)
|
||||||
|
y p)
|
||||||
|
(temp-state-aref b (mod (+ x 2) +keccak-state-columns+)
|
||||||
|
y p)))))))
|
||||||
|
(dotimes-unrolled (p +keccak-state-splits+)
|
||||||
|
(setf (state-aref a 0 0 p)
|
||||||
|
(logxor (state-aref a 0 0 p)
|
||||||
|
(keccak-f-round-constant i p)))))))
|
||||||
|
a))
|
||||||
|
|
||||||
|
#+cmu
|
||||||
|
(eval-when (:compile-toplevel)
|
||||||
|
(setq ext:*inline-expansion-limit* *old-expansion-limit*))
|
||||||
266
keccak-64bit.lisp
Normal file
266
keccak-64bit.lisp
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
;;;; SHA3 --- Secure Hash Algorithm 3 (Keccak) Implementation
|
||||||
|
;;;;
|
||||||
|
;;;; Copyright (C) 2012 PMSF IT Consulting Pierre R. Mai.
|
||||||
|
;;;;
|
||||||
|
;;;; Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
;;;; a copy of this software and associated documentation files (the
|
||||||
|
;;;; "Software"), to deal in the Software without restriction, including
|
||||||
|
;;;; without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
;;;; distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
;;;; permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
;;;; the following conditions:
|
||||||
|
;;;;
|
||||||
|
;;;; The above copyright notice and this permission notice shall be
|
||||||
|
;;;; included in all copies or substantial portions of the Software.
|
||||||
|
;;;;
|
||||||
|
;;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
;;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
;;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
;;;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
;;;; OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
;;;; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
;;;; OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
;;;;
|
||||||
|
;;;; Except as contained in this notice, the name of the author shall
|
||||||
|
;;;; not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
;;;; other dealings in this Software without prior written authorization
|
||||||
|
;;;; from the author.
|
||||||
|
;;;;
|
||||||
|
;;;; $Id$
|
||||||
|
|
||||||
|
(cl:in-package #:sha3)
|
||||||
|
|
||||||
|
;;;; %File Description:
|
||||||
|
;;;;
|
||||||
|
;;;; This file contains an implementation of Keccak 1600 tuned to
|
||||||
|
;;;; implementations that support unboxed arithmetic on (unsigned-byte
|
||||||
|
;;;; 64).
|
||||||
|
;;;;
|
||||||
|
;;;; Implementation Choices:
|
||||||
|
;;;;
|
||||||
|
;;;; This is a fairly straightforward implementation of Keccak 1600,
|
||||||
|
;;;; mostly tuned to sbcl for x86-64. It employs a bit of loop
|
||||||
|
;;;; unrolling at compile-time. More advanced implementation
|
||||||
|
;;;; techniques like plane-per-plane processing, lane complementing
|
||||||
|
;;;; and early parity (c.f. the Keccak implementation overview) did
|
||||||
|
;;;; not seem to work well with SBCL's compiler, resulting in slower,
|
||||||
|
;;;; less readable code.
|
||||||
|
;;;;
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Additional Keccak-f-1600 definitions
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(deftype keccak-1600-state ()
|
||||||
|
"Type of a keccak working state object for Keccak-1600."
|
||||||
|
`(simple-array keccak-1600-lane
|
||||||
|
(,+keccak-state-lanes+)))
|
||||||
|
|
||||||
|
(declaim (inline make-keccak-1600-state)
|
||||||
|
(ftype (function () keccak-1600-state) make-keccak-1600-state))
|
||||||
|
(defun make-keccak-1600-state ()
|
||||||
|
(declare #.*optimize-declaration*)
|
||||||
|
(make-array '(#.+keccak-state-lanes+)
|
||||||
|
:element-type 'keccak-1600-lane
|
||||||
|
:initial-element 0))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Transforming linear input/output to state array
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(defun keccak-state-merge-input (state bit-rate input start)
|
||||||
|
(declare (type keccak-1600-state state) (type (integer 0 1600) bit-rate)
|
||||||
|
(type (simple-array (unsigned-byte 8) (*)) input)
|
||||||
|
(type fixnum start)
|
||||||
|
#.*optimize-declaration*)
|
||||||
|
(let ((rate-bytes (truncate bit-rate 8)))
|
||||||
|
(declare (type (integer 0 200) rate-bytes))
|
||||||
|
(dotimes (y +keccak-state-rows+)
|
||||||
|
(dotimes (x +keccak-state-columns+)
|
||||||
|
(let* ((element (+ (* y +keccak-state-columns+) x))
|
||||||
|
(offset (* element +keccak-1600-lane-byte-width+))
|
||||||
|
(index (the fixnum (+ start offset))))
|
||||||
|
(when (>= offset rate-bytes)
|
||||||
|
(return-from keccak-state-merge-input))
|
||||||
|
(setf (aref state element)
|
||||||
|
(logxor
|
||||||
|
(aref state element)
|
||||||
|
.
|
||||||
|
#.(loop for byte-index from 0
|
||||||
|
below +keccak-1600-lane-byte-width+
|
||||||
|
collect
|
||||||
|
`(the keccak-1600-lane
|
||||||
|
(ash (aref input (+ index ,byte-index))
|
||||||
|
,(* byte-index 8)))))))))))
|
||||||
|
|
||||||
|
(defun keccak-state-extract-output (state output-bits)
|
||||||
|
(let* ((output-bytes (truncate output-bits 8))
|
||||||
|
(digest (make-array (list output-bytes) :element-type '(unsigned-byte 8))))
|
||||||
|
(dotimes (x +keccak-state-columns+)
|
||||||
|
(dotimes (y +keccak-state-rows+)
|
||||||
|
(let* ((element (+ (* y +keccak-state-columns+) x))
|
||||||
|
(offset (* element +keccak-1600-lane-byte-width+)))
|
||||||
|
(unless (>= offset output-bytes)
|
||||||
|
(loop with value = (aref state element)
|
||||||
|
for index from offset
|
||||||
|
below (min (+ offset +keccak-1600-lane-byte-width+) output-bytes)
|
||||||
|
do
|
||||||
|
(setf (aref digest index) (ldb (byte 8 0) value)
|
||||||
|
value (ash value -8)))))))
|
||||||
|
digest))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Keccak Constants
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(declaim (inline keccak-f-round-constant)
|
||||||
|
(ftype (function ((integer 0 23)) keccak-1600-lane) keccak-f-round-constant))
|
||||||
|
(defun keccak-f-round-constant (i)
|
||||||
|
(declare (type (integer 0 23) i)
|
||||||
|
#.*optimize-declaration*)
|
||||||
|
(let ((constants (load-time-value *keccak-f-round-constants* t)))
|
||||||
|
(declare (type (simple-array keccak-1600-lane (24)) constants))
|
||||||
|
(aref constants i)))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Helper: Rotation
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(declaim (inline keccak-f-rot)
|
||||||
|
(ftype (function (keccak-1600-lane (integer 0 63)) keccak-1600-lane)
|
||||||
|
keccak-f-rot))
|
||||||
|
(defun keccak-f-rot (value offset)
|
||||||
|
(declare (type (integer 0 63) offset)
|
||||||
|
(type keccak-1600-lane value)
|
||||||
|
#.*optimize-declaration*)
|
||||||
|
#+sbcl
|
||||||
|
(sb-rotate-byte:rotate-byte offset (byte 64 0) value)
|
||||||
|
#-sbcl
|
||||||
|
(if (zerop offset)
|
||||||
|
value
|
||||||
|
(logior (ldb (byte 64 0) (ash value offset))
|
||||||
|
(ash value (- offset 64)))))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; State and Temporary Variable Accessors
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(defmacro with-state-accessors ((&rest states) &body body)
|
||||||
|
"Bind the contents of the state(s) array(s) to local variables, and save
|
||||||
|
the content on normal form exit."
|
||||||
|
(let ((bindings nil) (mappings nil) (save-forms nil))
|
||||||
|
(loop for state in states
|
||||||
|
for map = (make-array '(#.+keccak-state-columns+ #.+keccak-state-rows+))
|
||||||
|
do
|
||||||
|
(dotimes (y +keccak-state-rows+)
|
||||||
|
(dotimes (x +keccak-state-columns+)
|
||||||
|
(let ((sym (make-symbol (format nil "~A-~D-~D" state x y))))
|
||||||
|
(setf (aref map x y) sym)
|
||||||
|
(push `(,sym (aref ,state ,(+ x (* y +keccak-state-columns+))))
|
||||||
|
bindings)
|
||||||
|
(push `(setf (aref ,state ,(+ x (* y +keccak-state-columns+))) ,sym)
|
||||||
|
save-forms))))
|
||||||
|
(push (cons state map) mappings))
|
||||||
|
`(let (,@bindings)
|
||||||
|
(declare (ignorable ,@(mapcar #'car bindings))
|
||||||
|
(type keccak-1600-lane ,@(mapcar #'car bindings)))
|
||||||
|
(macrolet ((state-aref (state x y &environment env)
|
||||||
|
(let ((entry (assoc state ',mappings)))
|
||||||
|
(unless entry (error "Strange: ~S!" state))
|
||||||
|
(aref (cdr entry)
|
||||||
|
(eval (trivial-macroexpand-all x env))
|
||||||
|
(eval (trivial-macroexpand-all y env))))))
|
||||||
|
(multiple-value-prog1 (progn ,@body)
|
||||||
|
,@save-forms)))))
|
||||||
|
|
||||||
|
(defmacro with-temp-state ((&rest temps) &body body)
|
||||||
|
"Bind local variables for each temporary state."
|
||||||
|
(let ((bindings nil) (mappings nil))
|
||||||
|
(loop for temp in temps
|
||||||
|
for map = (make-array '(#.+keccak-state-columns+ #.+keccak-state-rows+))
|
||||||
|
do
|
||||||
|
(dotimes (y +keccak-state-rows+)
|
||||||
|
(dotimes (x +keccak-state-columns+)
|
||||||
|
(let ((sym (make-symbol (format nil "~A-~D-~D" temp x y))))
|
||||||
|
(setf (aref map x y) sym)
|
||||||
|
(push `(,sym 0) bindings))))
|
||||||
|
(push (cons temp map) mappings))
|
||||||
|
`(let (,@bindings)
|
||||||
|
(declare (ignorable ,@(mapcar #'car bindings))
|
||||||
|
(type keccak-1600-lane ,@(mapcar #'car bindings)))
|
||||||
|
(macrolet ((temp-state-aref (temp x y &environment env)
|
||||||
|
(let ((entry (assoc temp ',mappings)))
|
||||||
|
(unless entry (error "Strange: ~S!" temp))
|
||||||
|
(aref (cdr entry)
|
||||||
|
(eval (trivial-macroexpand-all x env))
|
||||||
|
(eval (trivial-macroexpand-all y env))))))
|
||||||
|
,@body))))
|
||||||
|
|
||||||
|
(defmacro with-temp-rows ((&rest rows) &body body)
|
||||||
|
"Bind local variables for each temporary row."
|
||||||
|
(let ((bindings nil) (mappings nil))
|
||||||
|
(loop for row in rows
|
||||||
|
for map = (make-array '(#.+keccak-state-columns+))
|
||||||
|
do
|
||||||
|
(dotimes (x +keccak-state-columns+)
|
||||||
|
(let ((sym (make-symbol (format nil "~A-~D" row x))))
|
||||||
|
(setf (aref map x) sym)
|
||||||
|
(push `(,sym 0) bindings)))
|
||||||
|
(push (cons row map) mappings))
|
||||||
|
`(let (,@bindings)
|
||||||
|
(declare (ignorable ,@(mapcar #'car bindings))
|
||||||
|
(type keccak-1600-lane ,@(mapcar #'car bindings)))
|
||||||
|
(macrolet ((temp-row-aref (row x &environment env)
|
||||||
|
(let ((entry (assoc row ',mappings)))
|
||||||
|
(unless entry (error "Strange: ~S!" row))
|
||||||
|
(aref (cdr entry)
|
||||||
|
(eval (trivial-macroexpand-all x env))))))
|
||||||
|
,@body))))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Keccak-f permutation
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(declaim (ftype (function (keccak-1600-state) keccak-1600-state) keccak-f))
|
||||||
|
(defun keccak-f (a)
|
||||||
|
(declare (type keccak-1600-state a)
|
||||||
|
#.*optimize-declaration*)
|
||||||
|
(with-state-accessors (a)
|
||||||
|
(with-temp-state (b)
|
||||||
|
(with-temp-rows (c d)
|
||||||
|
(dotimes (i #.(+ 12 (* 2 (truncate (log +keccak-1600-lane-width+ 2)))))
|
||||||
|
(dotimes-unrolled (x +keccak-state-columns+)
|
||||||
|
(setf (temp-row-aref c x)
|
||||||
|
(logxor (state-aref a x 0)
|
||||||
|
(state-aref a x 1)
|
||||||
|
(state-aref a x 2)
|
||||||
|
(state-aref a x 3)
|
||||||
|
(state-aref a x 4))))
|
||||||
|
(dotimes-unrolled (x +keccak-state-columns+)
|
||||||
|
(setf (temp-row-aref d x)
|
||||||
|
(logxor (temp-row-aref c (mod (+ +keccak-state-columns+ (1- x))
|
||||||
|
+keccak-state-columns+))
|
||||||
|
(keccak-f-rot
|
||||||
|
(temp-row-aref c (mod (1+ x) +keccak-state-columns+))
|
||||||
|
1))))
|
||||||
|
(dotimes-unrolled (x +keccak-state-columns+)
|
||||||
|
(dotimes-unrolled (y +keccak-state-rows+)
|
||||||
|
(setf (state-aref a x y)
|
||||||
|
(logxor (state-aref a x y) (temp-row-aref d x)))))
|
||||||
|
(dotimes-unrolled (x +keccak-state-columns+)
|
||||||
|
(dotimes-unrolled (y +keccak-state-rows+)
|
||||||
|
(setf (temp-state-aref b y
|
||||||
|
(mod (+ (* 2 x) (* 3 y)) +keccak-state-rows+))
|
||||||
|
(keccak-f-rot (state-aref a x y)
|
||||||
|
(get-rotate-offset x y)))))
|
||||||
|
(dotimes-unrolled (x +keccak-state-columns+)
|
||||||
|
(dotimes-unrolled (y +keccak-state-rows+)
|
||||||
|
(setf (state-aref a x y)
|
||||||
|
(logxor (temp-state-aref b x y)
|
||||||
|
(logandc1
|
||||||
|
(temp-state-aref b (mod (1+ x) +keccak-state-columns+) y)
|
||||||
|
(temp-state-aref b (mod (+ x 2) +keccak-state-columns+)
|
||||||
|
y))))))
|
||||||
|
(setf (state-aref a 0 0) (logxor (state-aref a 0 0)
|
||||||
|
(keccak-f-round-constant i))))))
|
||||||
|
a))
|
||||||
46
pkgdef.lisp
Normal file
46
pkgdef.lisp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
;;;; SHA3 --- Secure Hash Algorithm 3 (Keccak) Implementation
|
||||||
|
;;;;
|
||||||
|
;;;; Copyright (C) 2012 PMSF IT Consulting Pierre R. Mai.
|
||||||
|
;;;;
|
||||||
|
;;;; Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
;;;; a copy of this software and associated documentation files (the
|
||||||
|
;;;; "Software"), to deal in the Software without restriction, including
|
||||||
|
;;;; without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
;;;; distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
;;;; permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
;;;; the following conditions:
|
||||||
|
;;;;
|
||||||
|
;;;; The above copyright notice and this permission notice shall be
|
||||||
|
;;;; included in all copies or substantial portions of the Software.
|
||||||
|
;;;;
|
||||||
|
;;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
;;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
;;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
;;;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
;;;; OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
;;;; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
;;;; OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
;;;;
|
||||||
|
;;;; Except as contained in this notice, the name of the author shall
|
||||||
|
;;;; not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
;;;; other dealings in this Software without prior written authorization
|
||||||
|
;;;; from the author.
|
||||||
|
;;;;
|
||||||
|
;;;; $Id$
|
||||||
|
|
||||||
|
;;;; %File Description:
|
||||||
|
;;;;
|
||||||
|
;;;; This file contains the package definition
|
||||||
|
;;;;
|
||||||
|
|
||||||
|
(cl:defpackage #:sha3
|
||||||
|
(:use #:common-lisp)
|
||||||
|
#+(or lispworks ccl)
|
||||||
|
(:shadow #:logxor)
|
||||||
|
(:export
|
||||||
|
;; Mid-Level
|
||||||
|
#:sha3-state #:sha3-state-p
|
||||||
|
#:sha3-init #:sha3-copy #:sha3-update #:sha3-final
|
||||||
|
;; High-Level
|
||||||
|
#:sha3-digest-vector #:sha3-digest-stream #:sha3-digest-file
|
||||||
|
))
|
||||||
67
sha3.asd
Normal file
67
sha3.asd
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
;;;; SHA3 --- Secure Hash Algorithm 3 (Keccak) Implementation
|
||||||
|
;;;;
|
||||||
|
;;;; Copyright (C) 2012 PMSF IT Consulting Pierre R. Mai.
|
||||||
|
;;;;
|
||||||
|
;;;; Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
;;;; a copy of this software and associated documentation files (the
|
||||||
|
;;;; "Software"), to deal in the Software without restriction, including
|
||||||
|
;;;; without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
;;;; distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
;;;; permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
;;;; the following conditions:
|
||||||
|
;;;;
|
||||||
|
;;;; The above copyright notice and this permission notice shall be
|
||||||
|
;;;; included in all copies or substantial portions of the Software.
|
||||||
|
;;;;
|
||||||
|
;;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
;;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
;;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
;;;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
;;;; OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
;;;; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
;;;; OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
;;;;
|
||||||
|
;;;; Except as contained in this notice, the name of the author shall
|
||||||
|
;;;; not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
;;;; other dealings in this Software without prior written authorization
|
||||||
|
;;;; from the author.
|
||||||
|
;;;;
|
||||||
|
;;;; $Id$
|
||||||
|
|
||||||
|
(cl:in-package #:cl-user)
|
||||||
|
|
||||||
|
;;;; %File Description:
|
||||||
|
;;;;
|
||||||
|
;;;; This file contains the system definition form for the SHA3
|
||||||
|
;;;; Library. System definitions use the ASDF system definition
|
||||||
|
;;;; facility.
|
||||||
|
;;;;
|
||||||
|
|
||||||
|
(asdf:defsystem "sha3"
|
||||||
|
:description "Secure Hash Algorithm 3 (Keccak) Implementation"
|
||||||
|
:author "Pierre R. Mai <pmai@pmsf.de>"
|
||||||
|
:maintainer "Pierre R. Mai <pmai@pmsf.de>"
|
||||||
|
:licence "MIT/X11"
|
||||||
|
:version "1.0.0"
|
||||||
|
#+sbcl :depends-on #+sbcl ("sb-rotate-byte")
|
||||||
|
:components ((:file "pkgdef")
|
||||||
|
(:file "common" :depends-on ("pkgdef"))
|
||||||
|
#+(and :sbcl (or :x86-64 :alpha))
|
||||||
|
(:file "keccak-64bit" :depends-on ("pkgdef" "common"))
|
||||||
|
#+(or (and :sbcl (not (or :x86-64 :alpha)))
|
||||||
|
:cmucl
|
||||||
|
(and :ccl :64-bit-target))
|
||||||
|
(:file "keccak-32bit" :depends-on ("pkgdef" "common"))
|
||||||
|
#-(or :sbcl :cmucl (and :ccl :64-bit-target))
|
||||||
|
(:file "keccak-16bit" :depends-on ("pkgdef" "common"))
|
||||||
|
(:file "sha3"
|
||||||
|
:depends-on ("pkgdef"
|
||||||
|
"common"
|
||||||
|
#+(and :sbcl (or :x86-64 :alpha))
|
||||||
|
"keccak-64bit"
|
||||||
|
#+(or (and :sbcl (not (or :x86-64 :alpha)))
|
||||||
|
:cmucl
|
||||||
|
(and :ccl :64-bit-target))
|
||||||
|
"keccak-32bit"
|
||||||
|
#-(or :sbcl :cmucl (and :ccl :64-bit-target))
|
||||||
|
"keccak-16bit"))))
|
||||||
248
sha3.lisp
Normal file
248
sha3.lisp
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
;;;; SHA3 --- Secure Hash Algorithm 3 (Keccak) Implementation
|
||||||
|
;;;;
|
||||||
|
;;;; Copyright (C) 2012 PMSF IT Consulting Pierre R. Mai.
|
||||||
|
;;;;
|
||||||
|
;;;; Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
;;;; a copy of this software and associated documentation files (the
|
||||||
|
;;;; "Software"), to deal in the Software without restriction, including
|
||||||
|
;;;; without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
;;;; distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
;;;; permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
;;;; the following conditions:
|
||||||
|
;;;;
|
||||||
|
;;;; The above copyright notice and this permission notice shall be
|
||||||
|
;;;; included in all copies or substantial portions of the Software.
|
||||||
|
;;;;
|
||||||
|
;;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
;;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
;;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
;;;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
;;;; OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
;;;; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
;;;; OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
;;;;
|
||||||
|
;;;; Except as contained in this notice, the name of the author shall
|
||||||
|
;;;; not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
;;;; other dealings in this Software without prior written authorization
|
||||||
|
;;;; from the author.
|
||||||
|
;;;;
|
||||||
|
;;;; $Id$
|
||||||
|
|
||||||
|
(cl:in-package #:sha3)
|
||||||
|
|
||||||
|
;;;; %File Description:
|
||||||
|
;;;;
|
||||||
|
;;;; This file contains the implementation of mid- and high-level
|
||||||
|
;;;; SHA-3 functions based on the optimized Keccak implementations.
|
||||||
|
;;;;
|
||||||
|
;;;; The main entry points are the mid-level functions sha3-init,
|
||||||
|
;;;; sha3-update and sha3-final to initialize, update and finalize an
|
||||||
|
;;;; sha3 hash, and sha3-copy in order to copy a sha3 state.
|
||||||
|
;;;;
|
||||||
|
;;;; For convenience high-level functions to hash a given vector,
|
||||||
|
;;;; stream or file are provided as sha3-digest-vector,
|
||||||
|
;;;; sha3-digest-stream and sha3-digest-vector.
|
||||||
|
;;;;
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Mid-Level Routines
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(defstruct (sha3-state
|
||||||
|
(:constructor make-sha3-state (bit-rate))
|
||||||
|
(:copier))
|
||||||
|
(state (make-keccak-1600-state) :type keccak-1600-state :read-only t)
|
||||||
|
(bit-rate 1024 :type (integer 0 1600) :read-only t)
|
||||||
|
(buffer (make-array 200 :element-type '(unsigned-byte 8)) :read-only t
|
||||||
|
:type (simple-array (unsigned-byte 8) (200)))
|
||||||
|
(buffer-index 0 :type (integer 0 199))
|
||||||
|
(finalized-p nil))
|
||||||
|
|
||||||
|
(defun sha3-init (&key (output-bit-length nil output-bit-length-p)
|
||||||
|
(bit-rate (if (and output-bit-length-p
|
||||||
|
output-bit-length)
|
||||||
|
(- 1600 (* 2 output-bit-length))
|
||||||
|
1024)))
|
||||||
|
"Create and return a new SHA-3 state. If `output-bit-length' is specified
|
||||||
|
then the state will run at the bit rate specified for the given output bit
|
||||||
|
length. If `output-bit-length' is unspecified, `bit-rate' can be specified
|
||||||
|
to select a suitable bit rate. If both are left unspecified then a default
|
||||||
|
bit rate of 1024 bits is selected, which is suitable for arbitrary output
|
||||||
|
bit lengths of up to 288 bits."
|
||||||
|
(check-type output-bit-length (member nil 224 256 288 384 512)
|
||||||
|
"Legal output-bit-length specifier for SHA-3/Keccak-1600")
|
||||||
|
(check-type bit-rate (member 576 832 1024 1088 1152)
|
||||||
|
"Legal bit-rate for SHA-3/Keccak-1600")
|
||||||
|
(if (or (null output-bit-length)
|
||||||
|
(= bit-rate (- 1600 (* 2 output-bit-length)))
|
||||||
|
(= bit-rate 1024))
|
||||||
|
(make-sha3-state bit-rate)
|
||||||
|
(error "Illegal combination of output-bit-length ~D and bit-rate ~D."
|
||||||
|
output-bit-length bit-rate)))
|
||||||
|
|
||||||
|
(defun sha3-copy (state)
|
||||||
|
"Return an independent copy of the SHA-3 state `state'."
|
||||||
|
(let ((result (make-sha3-state (sha3-state-bit-rate state))))
|
||||||
|
(setf (sha3-state-buffer-index result) (sha3-state-buffer-index state)
|
||||||
|
(sha3-state-finalized-p result) (sha3-state-finalized-p state))
|
||||||
|
(replace (sha3-state-buffer result) (sha3-state-buffer state))
|
||||||
|
(replace (sha3-state-state result) (sha3-state-state state))
|
||||||
|
result))
|
||||||
|
|
||||||
|
(defun sha3-update (state vector &key (start 0) (end (length vector)))
|
||||||
|
"Update the given SHA-3 state `state' from `vector', which must be
|
||||||
|
a simple-array with element-type (unsigned-byte 8), bounded by `start'
|
||||||
|
and `end', which must be numeric bounding-indices."
|
||||||
|
(declare (type sha3-state state)
|
||||||
|
(type (simple-array (unsigned-byte 8) (*)) vector)
|
||||||
|
(type fixnum start end)
|
||||||
|
(optimize (speed 3) (safety 1) (space 0) (debug 1)))
|
||||||
|
(let* ((keccak-state (sha3-state-state state))
|
||||||
|
(buffer (sha3-state-buffer state))
|
||||||
|
(buffer-index (sha3-state-buffer-index state))
|
||||||
|
(bit-rate (sha3-state-bit-rate state))
|
||||||
|
(rate-bytes (truncate bit-rate 8)))
|
||||||
|
(declare (type keccak-1600-state keccak-state)
|
||||||
|
(type (simple-array (unsigned-byte 8) (200)) buffer)
|
||||||
|
(type (integer 0 199) buffer-index)
|
||||||
|
(type (integer 0 1600) bit-rate)
|
||||||
|
(type (integer 0 200) rate-bytes)
|
||||||
|
#.*optimize-declaration*)
|
||||||
|
;; Handle potential remaining bytes
|
||||||
|
(unless (zerop buffer-index)
|
||||||
|
(let ((remainder (- (length buffer) buffer-index)))
|
||||||
|
(declare (type fixnum remainder))
|
||||||
|
(replace buffer vector :start1 buffer-index :start2 start :end2 end)
|
||||||
|
(when (>= (- end start) remainder)
|
||||||
|
(keccak-state-merge-input keccak-state bit-rate buffer 0)
|
||||||
|
(keccak-f keccak-state))
|
||||||
|
(setf (sha3-state-buffer-index state) 0
|
||||||
|
start (min (+ start remainder) end))))
|
||||||
|
;; Now handle full blocks, stuff any remainder into buffer
|
||||||
|
(loop for block-offset of-type fixnum from start to end by rate-bytes
|
||||||
|
do
|
||||||
|
(cond
|
||||||
|
((<= (+ block-offset rate-bytes) end)
|
||||||
|
(keccak-state-merge-input keccak-state bit-rate vector block-offset)
|
||||||
|
(keccak-f keccak-state))
|
||||||
|
(t
|
||||||
|
(replace buffer vector :start1 0 :start2 block-offset)
|
||||||
|
(setf (sha3-state-buffer-index state) (- end block-offset)))))))
|
||||||
|
|
||||||
|
(defun sha3-final (state &key (output-bit-length nil output-bit-length-p))
|
||||||
|
"If the given SHA-3 state `state' has not already been finalized,
|
||||||
|
finalize it by processing any remaining input in its buffer, with
|
||||||
|
suitable padding as specified by the SHA-3 standard. Returns the
|
||||||
|
message digest as a simple-array of (unsigned-byte 8). The length
|
||||||
|
of the returned digest is determined either by the output bit length
|
||||||
|
or bit rate specified on state creation, or for the special case of
|
||||||
|
default parameters being used, by the optional keyword argument
|
||||||
|
`output-bit-length'. If the state has previously been finalized,
|
||||||
|
the function will return the digest again."
|
||||||
|
(declare (type sha3-state state)
|
||||||
|
(type (or null (integer 0 1600)) output-bit-length)
|
||||||
|
(optimize (speed 3) (safety 1) (space 0) (debug 1)))
|
||||||
|
(let ((keccak-state (sha3-state-state state))
|
||||||
|
(buffer (sha3-state-buffer state))
|
||||||
|
(buffer-index (sha3-state-buffer-index state))
|
||||||
|
(bit-rate (sha3-state-bit-rate state))
|
||||||
|
(finalized-p (sha3-state-finalized-p state)))
|
||||||
|
(declare (type keccak-1600-state keccak-state)
|
||||||
|
(type (simple-array (unsigned-byte 8) (200)) buffer)
|
||||||
|
(type (integer 0 199) buffer-index)
|
||||||
|
(type (integer 0 1600) bit-rate)
|
||||||
|
(type (or null (simple-array (unsigned-byte 8) (*))) finalized-p)
|
||||||
|
#.*optimize-declaration*)
|
||||||
|
;; Determine output-bit-length
|
||||||
|
(if output-bit-length-p
|
||||||
|
(unless (or (= bit-rate 1024)
|
||||||
|
(= (* 2 output-bit-length) (- 1600 bit-rate)))
|
||||||
|
(error "Illegal output-bit-length ~D specified!" output-bit-length))
|
||||||
|
(setq output-bit-length (truncate (- 1600 bit-rate) 2)))
|
||||||
|
(cond
|
||||||
|
;; Check if already finalized
|
||||||
|
(finalized-p
|
||||||
|
(unless (= (* (length finalized-p) 8) output-bit-length)
|
||||||
|
(error "Mismatch in output-bit-length ~D in repeated call to sha3-final! ~
|
||||||
|
Should be: ~D!"
|
||||||
|
output-bit-length (* (length finalized-p) 8)))
|
||||||
|
finalized-p)
|
||||||
|
;; Finalize
|
||||||
|
(t
|
||||||
|
(keccak-state-merge-input keccak-state bit-rate
|
||||||
|
(pad-message-to-width
|
||||||
|
(subseq buffer 0 buffer-index)
|
||||||
|
bit-rate)
|
||||||
|
0)
|
||||||
|
(keccak-f keccak-state)
|
||||||
|
(setf (sha3-state-buffer-index state) 0
|
||||||
|
(sha3-state-finalized-p state)
|
||||||
|
(keccak-state-extract-output keccak-state output-bit-length))))))
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; High-Level Routines
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(defun sha3-digest-vector (vector &key (start 0) end
|
||||||
|
(output-bit-length 512))
|
||||||
|
"Calculate an SHA-3 message-digest of data in `vector', which should
|
||||||
|
be a 1d simple-array with element type (unsigned-byte 8), bounded by
|
||||||
|
`start' and `end'. The bit length of the message digest produced is
|
||||||
|
controlled by `output-bit-length', which can take on the values 224,
|
||||||
|
256, 288, 384 and 512, which is the default value."
|
||||||
|
(declare (optimize (speed 3) (safety 3) (space 0) (debug 1))
|
||||||
|
(type (simple-array (unsigned-byte 8) (*)) vector)
|
||||||
|
(type fixnum start)
|
||||||
|
(type (or null fixnum) end)
|
||||||
|
(type (integer 0 1600) output-bit-length))
|
||||||
|
(locally
|
||||||
|
(declare (optimize (safety 1) (debug 0)))
|
||||||
|
(let ((state (sha3-init :output-bit-length output-bit-length)))
|
||||||
|
(declare (type sha3-state state))
|
||||||
|
(let ((real-end (or end (length vector))))
|
||||||
|
(declare (type fixnum real-end))
|
||||||
|
(sha3-update state vector :start start :end real-end))
|
||||||
|
(sha3-final state))))
|
||||||
|
|
||||||
|
(eval-when (:compile-toplevel :load-toplevel :execute)
|
||||||
|
(defconstant +buffer-size+ (* 128 1024)
|
||||||
|
"Size of internal buffer to use for `sha3-digest-stream' and
|
||||||
|
`sha3-digest-file' operations."))
|
||||||
|
|
||||||
|
(deftype buffer-index () `(integer 0 ,+buffer-size+))
|
||||||
|
|
||||||
|
(defun sha3-digest-stream (stream &key (output-bit-length 512))
|
||||||
|
"Calculate an SHA-3 message-digest of data read from `stream', which
|
||||||
|
should be a stream with element type (unsigned-byte 8). The bit
|
||||||
|
length of the message digest produced is controlled by
|
||||||
|
`output-bit-length', which can take on the values 224, 256, 288, 384
|
||||||
|
and 512, which is the default value."
|
||||||
|
(declare (optimize (speed 3) (safety 3) (space 0) (debug 1))
|
||||||
|
(type stream stream)
|
||||||
|
(type (integer 0 1600) output-bit-length))
|
||||||
|
(locally
|
||||||
|
(declare (optimize (safety 1) (debug 0)))
|
||||||
|
(unless (equal (stream-element-type stream) '(unsigned-byte 8))
|
||||||
|
(error "Illegal stream-element-type ~S, must be ~S."
|
||||||
|
(stream-element-type stream) '(unsigned-byte 8)))
|
||||||
|
(let ((buffer (make-array '(#.+buffer-size+) :element-type '(unsigned-byte 8)))
|
||||||
|
(state (sha3-init :output-bit-length output-bit-length)))
|
||||||
|
(declare (type sha3-state state)
|
||||||
|
(type (simple-array (unsigned-byte 8) (#.+buffer-size+)) buffer))
|
||||||
|
(loop for bytes of-type buffer-index = (read-sequence buffer stream)
|
||||||
|
do (sha3-update state buffer :end bytes)
|
||||||
|
until (< bytes +buffer-size+)
|
||||||
|
finally
|
||||||
|
(return (sha3-final state))))))
|
||||||
|
|
||||||
|
(defun sha3-digest-file (pathname &key (output-bit-length 512))
|
||||||
|
"Calculate an SHA-3 message-digest of the file specified by
|
||||||
|
`pathname'. The bit length of the message digest produced is
|
||||||
|
controlled by `output-bit-length', which can take on the values 224,
|
||||||
|
256, 288, 384 and 512, which is the default value."
|
||||||
|
(declare (optimize (speed 3) (safety 3) (space 0) (debug 1))
|
||||||
|
(type (integer 0 1600) output-bit-length))
|
||||||
|
(locally
|
||||||
|
(declare (optimize (safety 1) (debug 0)))
|
||||||
|
(with-open-file (stream pathname :element-type '(unsigned-byte 8))
|
||||||
|
(sha3-digest-stream stream :output-bit-length output-bit-length))))
|
||||||
Reference in New Issue
Block a user