packages feed

Crypto-4.2.5.2: src/Data/Digest/SHA2.hs

{-# OPTIONS_GHC -funbox-strict-fields #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  Data.Digest.SHA2
-- Copyright   :  (c) Russell O'Connor 2006
-- License     :  BSD-style (see the file ReadMe.tex)
--
-- Implements SHA-256, SHA-384, SHA-512, and SHA-224 as defined in FIPS 180-2
-- <http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf>.
--
-----------------------------------------------------------------------------

module Data.Digest.SHA2
    ( -- * SHA-224
      sha224, sha224Ascii, Hash224
      -- * SHA-256
    , sha256, sha256Ascii, Hash256
      -- * SHA-384
    , sha384, sha384Ascii, Hash384
      -- * SHA-512
    , sha512, sha512Ascii, Hash512
      -- * Utilities
    , toOctets
    ) where

import Data.Word
import Data.Bits
import Data.List
import Numeric

ch :: Bits a => a -> a -> a -> a
ch x y z = (x .&. y) `xor` (complement x .&. z)

maj :: Bits a => a -> a -> a -> a
maj x y z = (x .&. y) `xor` (x .&. z) `xor` (y .&. z)

class (Num w, Bits w) => ShaData w where
  bigSigma0 :: w -> w
  bigSigma1 :: w -> w
  smallSigma0 :: w -> w
  smallSigma1 :: w -> w
  ks :: [w]

instance ShaData Word32 where
 bigSigma0 x = rotateR x 2 `xor` rotateR x 13 `xor` rotateR x 22
 bigSigma1 x = rotateR x 6 `xor` rotateR x 11 `xor` rotateR x 25
 smallSigma0 x = rotateR x 7 `xor` rotateR x 18 `xor` shiftR x 3
 smallSigma1 x = rotateR x 17 `xor` rotateR x 19 `xor` shiftR x 10
 ks =
   [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5
   ,0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174
   ,0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da
   ,0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967
   ,0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85
   ,0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070
   ,0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3
   ,0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2]

instance ShaData Word64 where
 bigSigma0 x = rotateR x 28 `xor` rotateR x 34 `xor` rotateR x 39
 bigSigma1 x = rotateR x 14 `xor` rotateR x 18 `xor` rotateR x 41
 smallSigma0 x = rotateR x 1 `xor` rotateR x 8 `xor` shiftR x 7
 smallSigma1 x = rotateR x 19 `xor` rotateR x 61 `xor` shiftR x 6
 ks =
   [0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc
   ,0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118
   ,0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2
   ,0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694
   ,0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65
   ,0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5
   ,0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4
   ,0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70
   ,0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df
   ,0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b
   ,0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30
   ,0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8
   ,0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8
   ,0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3
   ,0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec
   ,0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b
   ,0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178
   ,0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b
   ,0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c
   ,0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817]

blockSize = 16

-----------------------------------------------------------------------------
-- | 'padding' currently requires that the 'bitSize' of @a@ divide the 'bitSize'
-- of @w@
-----------------------------------------------------------------------------
padding :: (ShaData w, Bits a, Integral a) => [a] -> [[w]]
padding x = unfoldr block $ paddingHelper x 0 (0::Int) (0::Integer)
 where
  block [] = Nothing
  block x = Just $ splitAt blockSize x
  paddingHelper x o on n | on == (bitSize o) = o:paddingHelper x 0 0 n
  paddingHelper (x:xs) o on n | on < (bitSize o) =
    paddingHelper xs ((shiftL o bs) .|. (fromIntegral x)) (on+bs) $! (n+fromIntegral bs)
   where
    bs = bitSize x
  paddingHelper [] o on n = (shiftL (shiftL o 1 .|. 1) (bso-on-1)):
                            (zeros ((-(fromIntegral n-on+3*bso)) `mod` (blockSize*bso)))
                            [fromIntegral (shiftR n bso), fromIntegral n]
   where
    bso = bitSize o
    zeros 0 = id
    zeros n | 0 < n = let z=0 in (z:) . (zeros (n-bitSize z))

data Hash8 w = Hash8 !w !w !w !w !w !w !w !w deriving (Eq, Ord)

type Hash256 = Hash8 Word32
type Hash512 = Hash8 Word64

data Hash384 = Hash384 !Word64 !Word64 !Word64 !Word64 !Word64 !Word64 deriving (Eq, Ord)
data Hash224 = Hash224 !Word32 !Word32 !Word32 !Word32 !Word32 !Word32 !Word32 deriving (Eq, Ord)

instance (Integral a, Show a) => Show (Hash8 a) where
 showsPrec _ (Hash8 a b c d e f g h) =
  (showHex a) . (' ':) .
  (showHex b) . (' ':) .
  (showHex c) . (' ':) .
  (showHex d) . (' ':) .
  (showHex e) . (' ':) .
  (showHex f) . (' ':) .
  (showHex g) . (' ':) .
  (showHex h)

instance Show Hash384 where
 showsPrec _ (Hash384 a b c d e f) =
  (showHex a) . (' ':) .
  (showHex b) . (' ':) .
  (showHex c) . (' ':) .
  (showHex d) . (' ':) .
  (showHex e) . (' ':) .
  (showHex f)

instance Show Hash224 where
 showsPrec _ (Hash224 a b c d e f g) =
  (showHex a) . (' ':) .
  (showHex b) . (' ':) .
  (showHex c) . (' ':) .
  (showHex d) . (' ':) .
  (showHex e) . (' ':) .
  (showHex f) . (' ':) .
  (showHex g)

class (Eq h, Ord h, Show h) => Hash h where
  toOctets :: h -> [Word8]

bitsToOctets x = helper (bitSize x) x []
   where
    helper s x r | s <= 0 = r
                 | otherwise = helper (s-bs) (shiftR x bs) ((fromIntegral x):r)
     where
      bs = bitSize (head r)

instance (Integral h, Bits h, Show h) => Hash (Hash8 h) where
  toOctets (Hash8 x0 x1 x2 x3 x4 x5 x6 x7) = bitsToOctets =<< [x0, x1, x2, x3, x4, x5, x6, x7]

instance Hash Hash384 where
  toOctets (Hash384 x0 x1 x2 x3 x4 x5) = bitsToOctets =<< [x0, x1, x2, x3, x4, x5]

instance Hash Hash224 where
  toOctets (Hash224 x0 x1 x2 x3 x4 x5 x6) = bitsToOctets =<< [x0, x1, x2, x3, x4, x5, x6]

shaStep :: (ShaData w) => Hash8 w -> [w] -> Hash8 w
shaStep h m = (foldl' (flip id) h (zipWith mkStep3 ks ws)) `plus` h
 where
  ws = m++zipWith4 smallSigma (drop (blockSize-2) ws) (drop (blockSize-7) ws)
                              (drop (blockSize-15) ws) (drop (blockSize-16) ws)
   where
    smallSigma a b c d = smallSigma1 a + b + smallSigma0 c + d
  mkStep3 k w (Hash8 a b c d e f g h) = Hash8 (t1+t2) a b c (d+t1) e f g
   where
    t1 = h + bigSigma1 e + ch e f g + k + w
    t2 = bigSigma0 a + maj a b c
  (Hash8 x0 x1 x2 x3 x4 x5 x6 x7) `plus` (Hash8 y0 y1 y2 y3 y4 y5 y6 y7) =
    Hash8 (x0+y0) (x1+y1) (x2+y2) (x3+y3) (x4+y4) (x5+y5) (x6+y6) (x7+y7)

-----------------------------------------------------------------------------
-- | Due to the limitations of 'padding', 'sha' currently requires that the
-- bitSize of @a@ divide the 'bitSize' of @w@
-----------------------------------------------------------------------------
sha :: (ShaData w, Bits a, Integral a) => Hash8 w -> [a] -> Hash8 w
sha h0 x = foldl' shaStep h0 $ padding x

stringToOctets :: String -> [Word8]
stringToOctets = map (fromIntegral . fromEnum)

-----------------------------------------------------------------------------
-- | 'sha256' currently requires that the 'bitSize' of @a@ divide 32
-----------------------------------------------------------------------------
sha256 :: (Bits a, Integral a) => [a] -> Hash256
sha256 = sha $
  Hash8 0x6a09e667 0xbb67ae85 0x3c6ef372 0xa54ff53a 0x510e527f 0x9b05688c 0x1f83d9ab 0x5be0cd19

-----------------------------------------------------------------------------
-- | 'sha384' currently requires that the 'bitSize' of @a@ divide 64
-----------------------------------------------------------------------------
sha384 :: (Bits a, Integral a) => [a] -> Hash384
sha384 x = Hash384 x0 x1 x2 x3 x4 x5
 where
  Hash8 x0 x1 x2 x3 x4 x5 x6 x7 = flip sha x $
    Hash8 0xcbbb9d5dc1059ed8 0x629a292a367cd507 0x9159015a3070dd17 0x152fecd8f70e5939
          0x67332667ffc00b31 0x8eb44a8768581511 0xdb0c2e0d64f98fa7 0x47b5481dbefa4fa4

-----------------------------------------------------------------------------
-- | 'sha384' currently requires that the 'bitSize' of @a@ divide 64
-----------------------------------------------------------------------------
sha512 :: (Bits a, Integral a) => [a] -> Hash512
sha512 = sha $
  Hash8 0x6a09e667f3bcc908 0xbb67ae8584caa73b 0x3c6ef372fe94f82b 0xa54ff53a5f1d36f1
        0x510e527fade682d1 0x9b05688c2b3e6c1f 0x1f83d9abfb41bd6b 0x5be0cd19137e2179

-----------------------------------------------------------------------------
-- | 'sha224' currently requires that the 'bitSize' of @a@ divide 32
-----------------------------------------------------------------------------
sha224 :: (Bits a, Integral a) => [a] -> Hash224
sha224 x = Hash224 x0 x1 x2 x3 x4 x5 x6
 where
  Hash8 x0 x1 x2 x3 x4 x5 x6 x7 = flip sha x $
    Hash8 0xc1059ed8 0x367cd507 0x3070dd17 0xf70e5939 0xffc00b31 0x68581511 0x64f98fa7 0xbefa4fa4

-----------------------------------------------------------------------------
-- | 'sha256Ascii' assumes that all characters of the strings are
-- ISO-latin-1 characters.  ie. each characters fits in one octet.
-----------------------------------------------------------------------------
sha256Ascii :: String -> Hash256
sha256Ascii = sha256 . stringToOctets

-----------------------------------------------------------------------------
-- | 'sha384Ascii' assumes that all characters of the strings are
-- ISO-latin-1 characters.  ie. each characters fits in one octet.
-----------------------------------------------------------------------------
sha384Ascii :: String -> Hash384
sha384Ascii = sha384 . stringToOctets

-----------------------------------------------------------------------------
-- | 'sha512Ascii' assumes that all characters of the strings are
-- ISO-latin-1 characters.  ie. each characters fits in one octet.
-----------------------------------------------------------------------------
sha512Ascii :: String -> Hash512
sha512Ascii = sha512 . stringToOctets

-----------------------------------------------------------------------------
-- | 'sha224Ascii' assumes that all characters of the strings are
-- ISO-latin-1 characters.  ie. each characters fits in one octet.
-----------------------------------------------------------------------------
sha224Ascii :: String -> Hash224
sha224Ascii = sha224 . stringToOctets