packages feed

hs-blake2 (empty) → 0.0.1

raw patch · 11 files changed

+518/−0 lines, 11 filesdep +QuickCheckdep +basedep +bytestringsetup-changed

Dependencies added: QuickCheck, base, bytestring, bytestring-arbitrary, criterion, cryptohash, hs-blake2, tasty, tasty-quickcheck

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014, jay groven++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of jay groven nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ benches/BenchAll.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE OverloadedStrings #-}+module Main+( main+) where++import qualified Crypto.Hash.MD5 as MD5+import qualified Crypto.Hash.SHA1 as SHA1+import qualified Crypto.Hash.SHA256 as SHA2+import qualified Crypto.Hash.SHA3 as SHA3+import qualified Crypto.Hash.Skein256 as Skein+import qualified Crypto.Hash.Tiger as Tiger+import Data.ByteString ( hGetSome )+import System.IO+import Criterion.Main++import qualified Crypto.Hash.Tsuraan.Blake2.Parallel as ParBlake2+import qualified Crypto.Hash.Tsuraan.Blake2.Serial as SerBlake2+import qualified Crypto.Hash.Tsuraan.Blake2 as Blake2++main :: IO ()+main = do+  strings <- withFile "/dev/urandom" ReadMode $ \handle -> do+    -- let lens = [1000,2000,3000,4000,5000,6000,7000,8000,9000,10000]+    let lens = [1,10,100,1000,3000,6000,10000,30000,60000,100000]+    ss <- mapM (hGetSome handle) lens+    return $ zip lens ss+  defaultMain $+    [ bgroup (show sz)+             [ bench "Parallel" $ nf (ParBlake2.hash 32) st+             , bench "Sequential" $ nf (SerBlake2.hash 32) st+             , bench "Auto" $ nf (Blake2.hash 32) st+             , bench "MD5" $ nf MD5.hash st+             , bench "SHA1" $ nf SHA1.hash st+             , bench "SHA2" $ nf SHA2.hash st+             , bench "SHA3" $ nf (SHA3.hash 32) st+             , bench "Skein" $ nf (Skein.hash 32) st+             , bench "Tiger" $ nf Tiger.hash st+             ]+    | (sz, st) <- strings ]+
+ cbits/alloc.c view
@@ -0,0 +1,13 @@+#include <stdlib.h>+#include <blake2.h>++size_t blake2b_size()+{+  return sizeof(blake2b_state);+}++size_t blake2bp_size()+{+  return sizeof(blake2bp_state);+}+
+ cbits/alloc.h view
@@ -0,0 +1,2 @@+void *malloc_blake2b();+void *malloc_blake2bp();
+ hs-blake2.cabal view
@@ -0,0 +1,59 @@+-- Initial hs-blake2.cabal generated by cabal init.  For further +-- documentation, see http://haskell.org/cabal/users-guide/++name:                hs-blake2+version:             0.0.1+synopsis:            A cryptohash-inspired library for blake2+-- description:         +license:             BSD3+license-file:        LICENSE+author:              jay groven+maintainer:          tsuraan@gmail.com+-- copyright:           +category:            Data+build-type:          Simple+extra-source-files:  cbits/alloc.h+cabal-version:       >=1.10++library+  exposed-modules:     Crypto.Hash.Tsuraan.Blake2+                     , Crypto.Hash.Tsuraan.Blake2.Parallel+                     , Crypto.Hash.Tsuraan.Blake2.Serial+  other-modules:       Crypto.Hash.Tsuraan.Blake2.Internal+  c-sources:           cbits/alloc.c+  -- other-extensions:    +  build-depends:       base >=4.6 && < 4.8+                     , bytestring >=0.10+  hs-source-dirs:      src+  include-dirs:        cbits+  default-language:    Haskell2010+  ghc-options:         -Wall+  extra-libraries:     b2++benchmark benchmark-all+  type:                exitcode-stdio-1.0+  main-is:             BenchAll.hs+  build-depends:       base >=4.6 && < 4.8+                     , bytestring >=0.10+                     , cryptohash+                     , criterion+                     , hs-blake2+  hs-source-dirs:      benches+  default-language:    Haskell2010+  ghc-options:         -Wall+  extra-libraries:     b2++test-suite test-all+  type:                exitcode-stdio-1.0+  main-is:             TestAll.hs+  build-depends:       base >=4.6 && < 4.8+                     , bytestring >=0.10+                     , bytestring-arbitrary+                     , hs-blake2+                     , QuickCheck+                     , tasty+                     , tasty-quickcheck+  hs-source-dirs:      tests+  default-language:    Haskell2010+  ghc-options:         -Wall+  extra-libraries:     b2
+ src/Crypto/Hash/Tsuraan/Blake2.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE OverloadedStrings #-}+module Crypto.Hash.Tsuraan.Blake2+( hash+, hash_key+) where++import Data.ByteString ( ByteString, length )+import Prelude hiding ( length )++import qualified Crypto.Hash.Tsuraan.Blake2.Parallel as Par+import qualified Crypto.Hash.Tsuraan.Blake2.Serial as Ser++-- | Hash a strict 'ByteString' into a digest 'ByteString' using a key. This+-- will choose to use parallel or serial Blake2 depending on the size of the+-- input 'ByteString'.+hash_key :: ByteString -- ^The key to use when hashing+         -> Int        -- ^The digest size to generate; must be 1-64+         -> ByteString -- ^The 'ByteString' to hash+         -> ByteString+hash_key key hashlen bytes =+  if length bytes < cutoff+    then Ser.hash_key key hashlen bytes+    else Par.hash_key key hashlen bytes++-- | Hash a strict 'ByteString' into a digest 'ByteString'. This will choose to+-- use parallel or serial Blake2 depending on the size of the input+-- 'ByteString'+hash :: Int        -- ^The digest size to generate; must be 1-64+     -> ByteString -- ^The 'ByteString' to hash+     -> ByteString+hash hashlen bytes =+  if length bytes < cutoff+    then Ser.hash hashlen bytes+    else Par.hash hashlen bytes++-- This is a fairly sane cross-over point for when a hash is faster to+-- calculate in parallel than serially. This was found through experimentation,+-- so there's probably a smarter way to deal with it.+cutoff :: Int+cutoff = 5000+
+ src/Crypto/Hash/Tsuraan/Blake2/Internal.hs view
@@ -0,0 +1,77 @@+{-# LANGUAGE OverloadedStrings #-}+module Crypto.Hash.Tsuraan.Blake2.Internal+( BlakeState(..)+, runInit+, runInitKey+, runUpdate+, runFinalize+, runHasher+) where++import Data.ByteString.Internal ( create, toForeignPtr )+import Data.ByteString ( ByteString )+import System.IO.Unsafe ( unsafePerformIO )+import Foreign.ForeignPtr ( ForeignPtr, withForeignPtr, mallocForeignPtr )+import Foreign.Ptr ( Ptr, plusPtr )+import Foreign.C ( CInt(..) )+import Foreign.Storable ( Storable )+import Data.Word ( Word8 )++type BlakeHasher =+  Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Int -> Int -> Int -> IO CInt++data BlakeState a = BlakeState Int (ForeignPtr a) deriving ( Show )++runInit :: Storable a => (Ptr a -> Int -> IO CInt) -> Int -> IO (BlakeState a)+runInit ifn outlen = do+  fptr <- mallocForeignPtr+  withForeignPtr fptr $ \ptr -> do+    success <- ifn ptr outlen+    if success == 0+      then return $ BlakeState outlen fptr+      else error "runInit failure; probably a bad digest size"++runInitKey :: Storable a => (Ptr a -> Int -> Ptr Word8 -> Int -> IO CInt)+           -> ByteString -> Int -> IO (BlakeState a)+runInitKey ifn key outlen =+  let (kforeignptr, koff, klen) = toForeignPtr key+  in withForeignPtr kforeignptr $ \kptr -> do+      fptr <- mallocForeignPtr+      withForeignPtr fptr $ \ptr -> do+        success <- ifn ptr outlen (kptr `plusPtr` koff) klen+        if success == 0+          then return $ BlakeState outlen fptr+          else error "runInit failure; bad digest size or bad key size"++runUpdate :: (Ptr a -> Ptr Word8 -> Int -> IO CInt)+          -> BlakeState a -> ByteString -> IO ()+runUpdate ufn (BlakeState _ ctx) string =+  let (sforeignptr, soff, slen) = toForeignPtr string+  in withForeignPtr sforeignptr $ \sptr ->+      withForeignPtr ctx $ \cptr -> do+        _ <- ufn cptr (sptr `plusPtr` soff) slen+        return ()++runFinalize :: (Ptr a -> Ptr Word8 -> Int -> IO CInt)+            -> BlakeState a -> IO ByteString+runFinalize ffn (BlakeState hashlen fptr) =+  create hashlen $ \hptr ->+    withForeignPtr fptr $ \ctx -> do+      _ <- ffn ctx hptr hashlen+      return ()++runHasher :: BlakeHasher -> ByteString -> Int -> ByteString -> ByteString+runHasher hasher key hashlen bytes = unsafePerformIO $ create hashlen $ \hptr ->+  let (kforeignptr, koff, klen) = toForeignPtr key+      (bforeignptr, boff, blen) = toForeignPtr bytes+  in withForeignPtr kforeignptr $ \kptr ->+      withForeignPtr bforeignptr $ \bptr -> do+        result <- hasher hptr (bptr `plusPtr` boff) (kptr `plusPtr` koff)+                         hashlen blen klen+        if result /= 0+          then error "Blake2bp Error: probably bad key length or hash length"+          else return ()++-- Inline here gives us a ~15% speedup on ghc 7.8.2+{-# INLINE runHasher #-}+
+ src/Crypto/Hash/Tsuraan/Blake2/Parallel.hs view
@@ -0,0 +1,92 @@+{-# LANGUAGE OverloadedStrings #-}+module Crypto.Hash.Tsuraan.Blake2.Parallel+( Ctx+, init+, init_key+, update+, finalize+, hash+, hash_key+) where++import Data.ByteString ( ByteString )+import System.IO.Unsafe ( unsafePerformIO )+import Foreign.C ( CInt(..) )+import Foreign.Ptr ( Ptr )+import Foreign.Storable ( Storable(..) )+import Data.Word ( Word8 )++import Crypto.Hash.Tsuraan.Blake2.Internal ( BlakeState(..), runHasher, runInit, runInitKey, runUpdate, runFinalize )++import Prelude hiding ( init )++data Blake2bpState++-- |Opaque type that tracks the Blake2 hashing state. The update and finalize+-- functions mutate this context.+newtype Ctx = Ctx (BlakeState Blake2bpState) deriving ( Show )++instance Storable Blake2bpState where+  sizeOf _    = unsafePerformIO blake2bp_size+  alignment _ = 64 -- from blake2.h; this should be automagical, I think+  peek _      = error "no peek"+  poke _a _b  = error "no poke"++foreign import ccall "blake2.h blake2bp" blake2bp+  :: Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Int -> Int -> Int -> IO CInt++foreign import ccall "blake2.h blake2bp_init" blake2bp_init+  :: Ptr Blake2bpState -> Int -> IO CInt++foreign import ccall "blake2.h blake2bp_init_key" blake2bp_init_key+  :: Ptr Blake2bpState -> Int -> Ptr Word8 -> Int -> IO CInt++foreign import ccall "blake2.h blake2bp_update" blake2bp_update+  :: Ptr Blake2bpState -> Ptr Word8 -> Int -> IO CInt++foreign import ccall "blake2.h blake2bp_final" blake2bp_final+  :: Ptr Blake2bpState -> Ptr Word8 -> Int -> IO CInt++foreign import ccall "alloc.h blake2bp_size" blake2bp_size+  :: IO Int++-- |Create a hashing context.+init :: Int    -- ^Desired digest size+     -> IO Ctx+init outlen = Ctx `fmap` runInit blake2bp_init outlen++-- |Create a hashing context for key-based hashing.+init_key :: ByteString  -- ^Desired hashing key+         -> Int         -- ^Desired digest size+         -> IO Ctx+init_key key outlen = Ctx `fmap` runInitKey blake2bp_init_key key outlen++-- |Add more data to the hash.+update :: Ctx         -- ^Hashing context+       -> ByteString  -- ^Data to add to the hash+       -> IO ()+update (Ctx ptr) bs = runUpdate blake2bp_update ptr bs++-- |Finish hashing. This returns the digest of all the data that's been given+-- to the 'update' function.+finalize :: Ctx           -- ^Hashing context+         -> IO ByteString+finalize (Ctx state) = runFinalize blake2bp_final state++-- |Hash a 'ByteString' into a digest 'ByteString' using a key. This function+-- always runs in parallel, which is slower for very small strings but faster+-- as the strings get larger.+hash_key :: ByteString -- ^The key to hash with+         -> Int        -- ^The digest size to generate; must be 1-64+         -> ByteString -- ^The string to hash+         -> ByteString+hash_key key hashlen bytes = runHasher blake2bp key hashlen bytes++-- |Hash a 'ByteString' into a digest 'ByteString'. This function always runs+-- in parallel, which is slower for very small strings but faster as the+-- strings get larger.+hash :: Int        -- ^The digest size to generate; must be 1-64+     -> ByteString -- ^The string to hash+     -> ByteString+hash = hash_key ""+
+ src/Crypto/Hash/Tsuraan/Blake2/Serial.hs view
@@ -0,0 +1,92 @@+{-# LANGUAGE OverloadedStrings #-}+module Crypto.Hash.Tsuraan.Blake2.Serial+( Ctx+, init+, init_key+, update+, finalize+, hash+, hash_key+) where++import Data.ByteString ( ByteString )+import System.IO.Unsafe ( unsafePerformIO )+import Foreign.C ( CInt(..) )+import Foreign.Ptr ( Ptr )+import Foreign.Storable ( Storable(..) )+import Data.Word ( Word8 )++import Crypto.Hash.Tsuraan.Blake2.Internal ( BlakeState(..), runHasher, runInit, runInitKey, runUpdate, runFinalize )++import Prelude hiding ( init )++data Blake2bState++-- |Opaque type that tracks the Blake2 hashing state. The update and finalize+-- functions mutate this context.+newtype Ctx = Ctx (BlakeState Blake2bState) deriving ( Show )++instance Storable Blake2bState where+  sizeOf _    = unsafePerformIO blake2b_size+  alignment _ = 64 -- from blake2.h; this should be automagical, I think+  peek _      = error "no peek"+  poke _a _b  = error "no poke"++foreign import ccall "blake2.h blake2b" blake2b+  :: Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Int -> Int -> Int -> IO CInt++foreign import ccall "blake2.h blake2b_init" blake2b_init+  :: Ptr Blake2bState -> Int -> IO CInt++foreign import ccall "blake2.h blake2b_init_key" blake2b_init_key+  :: Ptr Blake2bState -> Int -> Ptr Word8 -> Int -> IO CInt++foreign import ccall "blake2.h blake2b_update" blake2b_update+  :: Ptr Blake2bState -> Ptr Word8 -> Int -> IO CInt++foreign import ccall "blake2.h blake2b_final" blake2b_final+  :: Ptr Blake2bState -> Ptr Word8 -> Int -> IO CInt++foreign import ccall "alloc.h blake2b_size" blake2b_size+  :: IO Int++-- |Create a hashing context.+init :: Int    -- ^Desired digest size+     -> IO Ctx+init outlen = Ctx `fmap` runInit blake2b_init outlen++-- |Create a hashing context for key-based hashing.+init_key :: ByteString  -- ^Desired hashing key+         -> Int         -- ^Desired digest size+         -> IO Ctx+init_key key outlen = Ctx `fmap` runInitKey blake2b_init_key key outlen++-- |Add more data to the hash.+update :: Ctx         -- ^Hashing context+       -> ByteString  -- ^Data to add to the hash+       -> IO ()+update (Ctx ptr) bs = runUpdate blake2b_update ptr bs++-- |Finish hashing. This returns the digest of all the data that's been given+-- to the 'update' function.+finalize :: Ctx           -- ^Hashing context+         -> IO ByteString+finalize (Ctx state) = runFinalize blake2b_final state++-- |Hash a 'ByteString' into a digest 'ByteString' using a key. This function+-- always runs in serial, which is faster for very small strings but slower as+-- the strings get larger.+hash_key :: ByteString -- ^The key to hash with+         -> Int        -- ^The digest size to generate; must be 1-64+         -> ByteString -- ^The string to hash+         -> ByteString+hash_key key hashlen bytes = runHasher blake2b key hashlen bytes++-- |Hash a 'ByteString' into a digest 'ByteString'. This function always runs+-- in serial, which is faster for very small strings but slower as the strings+-- get larger.+hash :: Int        -- ^The digest size to generate; must be 1-64+     -> ByteString -- ^The string to hash+     -> ByteString+hash = hash_key ""+
+ tests/TestAll.hs view
@@ -0,0 +1,70 @@+module Main+( main+) where++import qualified Data.ByteString as BS+import Data.ByteString.Arbitrary ( ArbByteString(..) )+import Data.ByteString ( ByteString )+import Control.Monad ( replicateM )++import qualified Crypto.Hash.Tsuraan.Blake2.Parallel as P+import qualified Crypto.Hash.Tsuraan.Blake2.Serial as S++import Test.QuickCheck+import Test.QuickCheck.Monadic+import Test.Tasty+import Test.Tasty.QuickCheck ( testProperty )++main :: IO ()+main = defaultMain $+  testGroup+    "Blake2"+    [ testGroup "Simple"+      [ testProperty "Parallel"+        $ update_equals_hash P.init P.update P.finalize P.hash+      , testProperty "Serial"+        $ update_equals_hash S.init S.update S.finalize S.hash+      ]+    , testGroup "Keyed"+      [ testProperty "Parallel"+        $ key_update_equals_hash P.init_key P.update P.finalize P.hash_key+      , testProperty "Serial"+        $ key_update_equals_hash S.init_key S.update S.finalize S.hash_key+      ]+    ]++update_equals_hash :: (Int -> IO a)+                   -> (a -> ByteString -> IO())+                   -> (a -> IO ByteString)+                   -> (Int -> ByteString -> ByteString)+                   -> Property+update_equals_hash ini upd fin hash = monadicIO $ do+  num  <- pick $ choose (1,20)+  strs <- replicateM num $ fromABS `fmap` pick arbitrary+  sz   <- pick $ choose (1,64)++  h <- run $ do+    ctx <- ini sz+    mapM_ (upd ctx) strs+    fin ctx+  assert $ h == hash sz (BS.concat strs)++key_update_equals_hash :: (ByteString -> Int -> IO a)+                       -> (a -> ByteString -> IO())+                       -> (a -> IO ByteString)+                       -> (ByteString -> Int -> ByteString -> ByteString)+                       -> Property+key_update_equals_hash ini upd fin hash = monadicIO $ do+  keylen <- pick $ choose (1,64)+  keyw8s <- replicateM keylen $ pick arbitrary+  num    <- pick $ choose (1,20)+  strs   <- replicateM num $ fromABS `fmap` pick arbitrary+  sz     <- pick $ choose (1,64)++  let key = BS.pack keyw8s+  h <- run $ do+    ctx <- ini key sz+    mapM_ (upd ctx) strs+    fin ctx+  assert $ h == hash key sz (BS.concat strs)+