{-# LANGUAGE CPP #-}
module Darcs.Util.Hash
( Hash(..)
, encodeBase16, decodeBase16, sha256, sha256sum, rawHash
, match
, sha1PS, SHA1(..), showAsHex, sha1Xor, sha1zero, sha1short
, sha1Show, sha1Read
) where
import Data.ByteArray ( convert )
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Char8 as BC
import qualified Data.ByteString.Base16 as B16
import qualified Crypto.Hash as H
import Data.Char( intToDigit, ord )
import Data.Binary ( Binary(..), decode, encode )
import Data.Bits ( xor, shiftL, (.|.) )
import Data.Word ( Word8, Word32 )
import Darcs.Prelude
data Hash = SHA256 !B.ByteString
| NoHash
deriving (Show, Eq, Ord, Read)
encodeBase16 :: Hash -> B.ByteString
encodeBase16 (SHA256 bs) = B16.encode bs
encodeBase16 NoHash = B.empty
decodeBase16 :: B.ByteString -> Hash
decodeBase16 bs
| B.length bs == 64
#if MIN_VERSION_base16_bytestring(1,0,0)
, Right dbs <- B16.decode bs = SHA256 dbs
#else
, (dbs, rest) <- B16.decode bs, B.null rest = SHA256 dbs
#endif
| otherwise = NoHash
sha256 :: BL.ByteString -> Hash
sha256 bits = SHA256 (convert (H.hashlazy bits :: H.Digest H.SHA256))
sha256sum :: B.ByteString -> String
sha256sum = BC.unpack . B16.encode . convert . H.hashWith H.SHA256
rawHash :: Hash -> B.ByteString
rawHash NoHash = error "Cannot obtain raw hash from NoHash."
rawHash (SHA256 s) = s
match :: Hash -> Hash -> Bool
NoHash `match` _ = False
_ `match` NoHash = False
x `match` y = x == y
data SHA1 = SHA1 !Word32 !Word32 !Word32 !Word32 !Word32
deriving (Eq,Ord)
instance Show SHA1 where
show = BC.unpack . sha1Show
instance Binary SHA1 where
put (SHA1 a b c d e) = put a >> put b >> put c >> put d >> put e
get = do a <- get; b <- get; c <- get; d <- get; e <- get; return (SHA1 a b c d e)
sha1Xor :: SHA1 -> SHA1 -> SHA1
sha1Xor (SHA1 a1 b1 c1 d1 e1) (SHA1 a2 b2 c2 d2 e2) =
SHA1 (a1 `xor` a2) (b1 `xor` b2) (c1 `xor` c2) (d1 `xor` d2) (e1 `xor` e2)
sha1zero :: SHA1
sha1zero = SHA1 0 0 0 0 0
sha1short :: SHA1 -> Word32
sha1short (SHA1 a _ _ _ _) = a
sha1PS:: B.ByteString -> SHA1
sha1PS = fromArray . convert . H.hashWith H.SHA1 where
fromArray = decode . BL.fromStrict
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)
sha1Read :: B.ByteString -> Maybe SHA1
sha1Read bs
| B.length bs == 40
, B.all is_hex bs =
Just $ SHA1 (readWord 0) (readWord 8) (readWord 16) (readWord 24) (readWord 32)
| otherwise = Nothing
where
readWord i = B.foldl' readByte 0 (B.take 8 (B.drop i bs))
readByte :: Word32 -> Word8 -> Word32
readByte r b = r `shiftL` 4 .|. (fromHex b)
fromHex :: Word8 -> Word32
fromHex b | btw_0_9 b = fromIntegral (b - ord_0)
| btw_a_f b = fromIntegral (b - ord_a) + 10
| otherwise = error "impossible case"
ord_0 :: Word8
ord_0 = fromIntegral (ord '0')
ord_9 :: Word8
ord_9 = fromIntegral (ord '9')
ord_a :: Word8
ord_a = fromIntegral (ord 'a')
ord_f :: Word8
ord_f = fromIntegral (ord 'f')
btw_0_9 b = b >= ord_0 && b <= ord_9
btw_a_f b = b >= ord_a && b <= ord_f
is_hex b = btw_0_9 b || btw_a_f b
{-# INLINE sha1Show #-}
sha1Show :: SHA1 -> B.ByteString
sha1Show = B16.encode . BL.toStrict . encode