packages feed

LslPlus-0.1.4: src/Language/Lsl/Internal/SHA1.hs

module Language.Lsl.Internal.SHA1(hashStoI,hashStoHex) where

import Text.Printf
import Codec.Binary.UTF8.String
import Data.Array(Array,array,(!))
import Data.Bits(Bits,(.|.),(.&.),xor,rotateL,shiftR,shiftL,complement)
import Data.List(foldl')
import Data.Word(Word8,Word32)

hashStoI = digestToInteger . hash . encode
hash = foldl' (\ buf block -> addDigest buf (processMessageBlock buf block)) ss . blocks512

hashStoHex :: String -> String
hashStoHex s = printf "%040X" (hashStoI s)

data Digest = Digest { eA :: !Word32,
                       eB :: !Word32,
                       eC :: !Word32,
                       eD :: !Word32,
                       eE :: !Word32 }
            deriving (Eq, Show)

addDigest (Digest a1 b1 c1 d1 e1) (Digest a2 b2 c2 d2 e2) = Digest (a1+a2) (b1+b2) (c1+c2) (d1+d2) (e1+e2)

digestToInteger :: Digest -> Integer
digestToInteger (Digest a b c d e) = let n = fromIntegral e +
                                            (fromIntegral d `shiftL` 32) +
                                            (fromIntegral c `shiftL` 64) +
                                            (fromIntegral b `shiftL` 96) +
                                            (fromIntegral a `shiftL` 128)
                                     in n

ks :: Array Int Word32
ks = array (0,79) $ zip [0..] $ concatMap (replicate 20) [0x5a827999,0x6ed9eba1,0x8f1bbcdc,0xca62c1d6]

fs :: Array Int (Word32 -> Word32 -> Word32 -> Word32)
fs  = array (0,79) $ zip [0..] $ concatMap (replicate 20) [ \ x y z -> (x .&. y) .|. ((complement x) .&. z),
                                                            \ x y z -> x `xor` y `xor` z,
                                                            \ x y z -> (x .&. y) .|. (x .&. z) .|. (y .&. z),
                                                            \ x y z -> x `xor` y `xor` z ]

processMessageBlock i words = 
    let foo' n i [] = i
        foo' n i (w:ws) = 
            let i' = (Digest ((eA i `rotateL` 5)  + ((fs ! n) (eB i) (eC i) (eD i)) + eE i + w + (ks ! n)) (eA i) (eB i `rotateL` 30) (eC i) (eD i))
            in foo' (n + 1) i' ws
    in foo' 0 i (compute80Ws words)
    
compute80Ws :: [Word32] -> [Word32]
compute80Ws xs = 
    let foo i (q:qs) rs
            | i < 16 = q : foo (i + 1) qs (q:rs)
            | otherwise = 
                let v = (rs !! 2 `xor` rs !! 7 `xor` rs !! 13`xor` rs !! 15) `rotateL` 1
                in v : foo (i + 1) qs (v:rs)
    in take 80 $ foo 0 (xs ++ repeat 0) []
    
ss :: Digest
ss = Digest 0x67452301 0xefcdab89 0x98badcfe 0x10325476 0xc3d2e1f0

getByte v i = fromIntegral $ (v `shiftR` (i*8)) .&. 0x0ff

padMessage :: [Word8] -> [Word8]
padMessage msg =
   let f :: Int -> [Word8] -> [Word8]
       f n [] = 0x80:((replicate (64 - ((n + 9) `mod` 64)) 0) ++ map (getByte (8*n)) [7,6..0])
       f n (b:bs) = b : (f (n+1) bs)
   in f 0 msg

bytesToWord :: [Word8] -> Word32
bytesToWord = go 0
   where go acc [] = acc
         go acc (b:bs) = go ((acc `shiftL` 8) .|. fromIntegral b) bs
   
chunk _ [] = []
chunk n l = take n l : chunk n (drop n l)
  
blocks512 :: [Word8] -> [[Word32]]
blocks512 = (map (map bytesToWord . chunk 4)) . chunk 64 . padMessage