module Bitcoin.Crypto.Hash.RipEmd160
( RipEmd160(..)
, ripemd160
, ripemdTest , testCases
)
where
import Data.Char
import Data.Int
import Data.Word
import Data.Bits
import qualified Data.ByteString as B
import Control.Monad
import Foreign
import System.IO.Unsafe as Unsafe
import Bitcoin.Misc.OctetStream
import Bitcoin.Misc.HexString
foreign import ccall safe "rmd160.c MDinit" c_MDInit :: Ptr Word32 -> IO ()
foreign import ccall safe "rmd160.c compress" c_compress :: Ptr Word32 -> Ptr Word32 -> IO ()
foreign import ccall safe "rmd160.c MDfinish" c_MDfinish :: Ptr Word32 -> Ptr Word8 -> Word32 -> Word32 -> IO ()
newtype RipEmd160 = RipEmd160 { unRipEmd160 :: B.ByteString } deriving (Eq,Ord)
instance Show RipEmd160 where show (RipEmd160 bs) = "RipEmd160<" ++ toHexStringChars bs ++ ">"
instance OctetStream RipEmd160 where
toByteString = unRipEmd160
fromByteString bs = case B.length bs of
20 -> RipEmd160 bs
_ -> error "RipEmd160/fromByteString: RipEmd160 is expected to be 20 bytes"
ripemd160 :: OctetStream a => a -> RipEmd160
ripemd160 msg = RipEmd160 $ Unsafe.unsafePerformIO (ripemd160_IO $ toByteString msg)
ripemd160_IO :: B.ByteString -> IO B.ByteString
ripemd160_IO msg = do
let n = B.length msg
k = div n 64
(remaining, chunks) = partition k msg
allocaBytes 20 $ \mdbuf -> do
c_MDInit mdbuf
forM_ chunks $ \chunk -> B.useAsCStringLen chunk $ \(ptr,_) -> c_compress mdbuf (castPtr ptr)
B.useAsCStringLen remaining $ \(ptr,_) -> c_MDfinish mdbuf (castPtr ptr) (fromIntegral n) 0
B.packCStringLen (castPtr mdbuf, 20)
partition :: Int -> B.ByteString -> (B.ByteString,[B.ByteString])
partition 0 msg = (msg,[])
partition k msg = let (rest,xs) = partition (k1) (B.drop 64 msg) in (rest, B.take 64 msg : xs)
ripemdTest :: [String]
ripemdTest = concatMap worker list where
list = zip [1..] testCases
worker (i,(msg,hexhash)) = result where
ourhash = toHexString' False $ ripemd160 msg
result = if hexhash==ourhash then [] else ["test case " ++ show i ++ " failed"]
testCases :: [(String,HexString)]
testCases = map (\(msg,hash) -> (msg, HexString hash))
[ ("" , "9c1185a5c5e9fc54612808977ee8f548b2258d31")
, ("a" , "0bdc9d2d256b3ee9daae347be6f4dc835a467ffe")
, ("abc" , "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc")
, ("message digest" , "5d0689ef49d2fae572b881b123a85ffa21595f36")
, ("abcdefghijklmnopqrstuvwxyz" , "f71c27109c692c1b56bbdceb5b9d2865b3708dbc")
, ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" , "12a053384a9c0c88e405a06c27dcf49ada62eb2b")
, ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" , "b0e20b6e3116640286ed3a87a5713079b21f5189")
, (concat (replicate 8 "1234567890") , "9b752e45573d4b39f4dbd3323cab82bf63326bfb")
, (replicate 1000000 'a' , "52783243c1697bdbe16d37f97f68f08325dc1528")
]