Copyright (C) 2001, 2004 Ian Lynagh This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. \begin{code} module SHA1 (sha1PS) where import Char (intToDigit) import Bits (xor, rotateL, shiftL, (.&.), (.|.), complement) import Word (Word32, Word8) import FastPackedString (PackedString, concatPS, packWords, lengthPS, indexPSW, nullPS, dropPS) #if __GLASGOW_HASKELL__ < 603 import Data.Array.Base (unsafeFreezeSTUArray) import Control.Monad.ST (runST) import Data.Array (Ix) #else import Data.Array.ST (runSTUArray) #endif import Control.Monad.ST (ST) import Data.Array.Unboxed (UArray, (!)) import Data.Array.ST (STUArray, readArray, writeArray, newArray_) data ABCDE = ABCDE !Word32 !Word32 !Word32 !Word32 !Word32 data XYZ = XYZ !Word32 !Word32 !Word32 sha1PS :: PackedString -> String sha1PS s = s5 where s1_2 = sha1_step_1_2_pad_length s abcde = sha1_step_3_init abcde' = sha1_step_4_main abcde s1_2 s5 = sha1_step_5_display abcde' \end{code} sha1_step_1_2_pad_length assumes the length is at most 2^61. This seems reasonable as the Int used to represent it is normally 32bit, but obviously could go wrong with large inputs on 64bit machines. The PackedString library should probably move to Word64s if this is an issue, though. \begin{code} sha1_step_1_2_pad_length :: PackedString -> PackedString sha1_step_1_2_pad_length s = let len = lengthPS s num_nuls = (55 - len) `mod` 64 padding = 128:replicate num_nuls 0 len_w8s = reverse $ size_split 8 (fromIntegral len*8) in concatPS [s, packWords padding, packWords len_w8s] size_split :: Int -> Integer -> [Word8] size_split 0 _ = [] size_split p n = fromIntegral d:size_split (p-1) n' where (n', d) = divMod n 256 sha1_step_3_init :: ABCDE sha1_step_3_init = ABCDE 0x67452301 0xefcdab89 0x98badcfe 0x10325476 0xc3d2e1f0 \end{code} \begin{code} sha1_step_4_main :: ABCDE -> PackedString -> ABCDE sha1_step_4_main abcde0@(ABCDE a b c d e) s | nullPS s = abcde0 | otherwise = sha1_step_4_main abcde5 (dropPS 64 s) where arr = mk_array s abcde1 = foldl (doit arr f1 0x5a827999) abcde0 [0..19] abcde2 = foldl (doit arr f2 0x6ed9eba1) abcde1 [20..39] abcde3 = foldl (doit arr f3 0x8f1bbcdc) abcde2 [40..59] abcde4 = foldl (doit arr f2 0xca62c1d6) abcde3 [60..79] f1 (XYZ x y z) = (x .&. y) .|. ((complement x) .&. z) f2 (XYZ x y z) = x `xor` y `xor` z f3 (XYZ x y z) = (x .&. y) .|. (x .&. z) .|. (y .&. z) ABCDE a' b' c' d' e' = abcde4 abcde5 = ABCDE (a + a') (b + b') (c + c') (d + d') (e + e') mk_array :: PackedString -> UArray Int Word32 mk_array ps = runSTUArray (mk_array' ps) mk_array' :: PackedString -> ST s (STUArray s Int Word32) mk_array' ps = do arr <- newArray_ (0, 79) mapM_ (set_array1 arr ps) [0..15] mapM_ (set_array2 arr) [16..79] return arr set_array1 :: STUArray s Int Word32 -> PackedString -> Int -> ST s () set_array1 arr ps i = writeArray arr i w where i' = 4 * i w = shiftL (fromIntegral $ indexPSW ps i' ) 24 + shiftL (fromIntegral $ indexPSW ps (i' + 1)) 16 + shiftL (fromIntegral $ indexPSW ps (i' + 2)) 8 + (fromIntegral $ indexPSW ps (i' + 3)) set_array2 :: STUArray s Int Word32 -> Int -> ST s () set_array2 arr i = do w1 <- readArray arr (i - 3) w2 <- readArray arr (i - 8) w3 <- readArray arr (i - 14) w4 <- readArray arr (i - 16) writeArray arr i (rotateL (w1 `xor` w2 `xor` w3 `xor` w4) 1) doit :: UArray Int Word32 -> (XYZ -> Word32) -> Word32 -> ABCDE -> Int -> ABCDE doit arr f k (ABCDE a b c d e) i = ABCDE a' a (rotateL b 30) c d where a' = rotateL a 5 + f (XYZ b c d) + e + (arr ! i) + k sha1_step_5_display :: ABCDE -> String sha1_step_5_display (ABCDE a b c d e) = concatMap showAsHex [a, b, c, d, e] showAsHex :: Word32 -> String showAsHex n = showIt 8 n "" where showIt :: Int -> Word32 -> String -> String showIt 0 _ r = r showIt i x r = case quotRem x 16 of (y, z) -> let c = intToDigit (fromIntegral z) in c `seq` showIt (i-1) y (c:r) #if __GLASGOW_HASKELL__ < 603 runSTUArray :: (Ix i) => (forall s . ST s (STUArray s i e)) -> UArray i e runSTUArray st = runST (st >>= unsafeFreezeSTUArray) #endif \end{code}