cryptohash 0.8.2 → 0.8.3
raw patch · 4 files changed
+117/−30 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Crypto.Hash: HMAC :: ByteString -> HMAC a
+ Crypto.Hash: data HMAC a
+ Crypto.Hash: hmac :: HashFunctionBS a -> Int -> ByteString -> ByteString -> HMAC a
+ Crypto.Hash: hmacToByteString :: HMAC a -> ByteString
+ Crypto.Hash: instance Eq (HMAC a)
+ Crypto.Hash: type HashFunctionBS a = ByteString -> Digest a
+ Crypto.Hash: type HashFunctionLBS a = ByteString -> Digest a
Files
- Crypto/Hash.hs +42/−0
- Crypto/MAC/HMAC.hs +6/−6
- Tests/KAT.hs +68/−23
- cryptohash.cabal +1/−1
Crypto/Hash.hs view
@@ -13,6 +13,8 @@ ( -- * Types HashAlgorithm(..)+ , HashFunctionBS+ , HashFunctionLBS , Context , Digest -- * Functions@@ -43,12 +45,17 @@ , Skein512_384 , Skein512_512 , Whirlpool+ -- * MAC algorithms+ , HMAC(..)+ , hmac ) where import Crypto.Hash.Types import Crypto.Hash.Utils import Data.ByteString (ByteString)+import Data.Bits (xor)+import Data.List (foldl') import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as L @@ -67,6 +74,12 @@ import qualified Crypto.Hash.Skein512 as Skein512 import qualified Crypto.Hash.Whirlpool as Whirlpool +-- | Alias to a single pass hash function that operate on a strict bytestring+type HashFunctionBS a = ByteString -> Digest a++-- | Alias to a single pass hash function that operate on a lazy bytestring+type HashFunctionLBS a = L.ByteString -> Digest a+ -- | run hashUpdates on one single bytestring and return the updated context. hashUpdate :: HashAlgorithm a => Context a -> ByteString -> Context a hashUpdate ctx b = hashUpdates ctx [b]@@ -126,3 +139,32 @@ DEFINE_INSTANCE_LEN(Skein512_256, Skein512, 256) DEFINE_INSTANCE_LEN(Skein512_384, Skein512, 384) DEFINE_INSTANCE_LEN(Skein512_512, Skein512, 512)++-- | Represent an HMAC that is phantom types with the hash used to produce the mac.+--+-- The Eq instance is constant time.+data HMAC a = HMAC { hmacToByteString :: ByteString -- ^ return the binary HMAC+ }++instance Eq (HMAC a) where+ (HMAC b1) == (HMAC b2)+ | B.length b1 /= B.length b2 = False+ | otherwise = foldl' (&&!) True $ B.zipWith (==) b1 b2+ where True &&! True = True+ True &&! False = False+ False &&! True = False+ False &&! False = False++-- | compute a MAC using the supplied hashing function+hmac :: HashFunctionBS a -- ^ Hash function to use+ -> Int -- ^ Block size in bytes of the hash function+ -> ByteString -- ^ Secret key+ -> ByteString -- ^ Message to MAC+ -> HMAC a+hmac hashF blockSize secret msg = HMAC $ digestToByteString $ hashF $ B.append opad (digestToByteString $ hashF $ B.append ipad msg)+ where opad = B.map (xor 0x5c) k'+ ipad = B.map (xor 0x36) k'++ k' = B.append kt pad+ kt = if B.length secret > fromIntegral blockSize then digestToByteString (hashF secret) else secret+ pad = B.replicate (fromIntegral blockSize - B.length kt) 0
Crypto/MAC/HMAC.hs view
@@ -18,10 +18,10 @@ -- | compute a MAC using the supplied hashing function hmac :: (ByteString -> ByteString) -> Int -> ByteString -> ByteString -> ByteString-hmac f blockSize secret msg = f $ B.append opad (f $ B.append ipad msg) where- opad = B.map (xor 0x5c) k'- ipad = B.map (xor 0x36) k'+hmac hashF blockSize secret msg = hashF $ B.append opad (hashF $ B.append ipad msg)+ where opad = B.map (xor 0x5c) k'+ ipad = B.map (xor 0x36) k' - k' = B.append kt pad- kt = if B.length secret > fromIntegral blockSize then f secret else secret- pad = B.replicate (fromIntegral blockSize - B.length kt) 0+ k' = B.append kt pad+ kt = if B.length secret > fromIntegral blockSize then hashF secret else secret+ pad = B.replicate (fromIntegral blockSize - B.length kt) 0
Tests/KAT.hs view
@@ -1,8 +1,11 @@-import Test.HUnit+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ViewPatterns #-} import Data.Char import Data.Bits import Data.Word+import Data.ByteString (ByteString) import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC import qualified Crypto.Hash.MD2 as MD2 import qualified Crypto.Hash.MD4 as MD4 import qualified Crypto.Hash.MD5 as MD5@@ -19,7 +22,14 @@ import qualified Crypto.Hash.Skein512 as Skein512 import qualified Crypto.Hash.Whirlpool as Whirlpool import Crypto.Hash+import qualified Crypto.MAC.HMAC as HMAC +import Test.Framework (Test, defaultMain, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.Framework.Providers.HUnit+import Test.HUnit ((@=?))++v0,v1,v2 :: ByteString v0 = "" v1 = "The quick brown fox jumps over the lazy dog" v2 = "The quick brown fox jumps over the lazy cog"@@ -161,6 +171,7 @@ hexaliseB :: B.ByteString -> B.ByteString hexaliseB = B.pack . hexalise . B.unpack +splitB :: Int -> ByteString -> [ByteString] splitB l b = if B.length b > l then@@ -175,36 +186,70 @@ runhash hash v = showHash $ (fctHash hash) $ v runhashinc hash v = showHash $ (fctInc hash) $ v -makeTestAlg (name, hash, results) = concatMap maketest $ zip3 [0..] vectors results+makeTestAlg (name, hash, results) = testGroup name $ concatMap maketest (zip3 [0..] vectors results) where- testname i = name ++ " " ++ show i- runtest v = runhash hash $ B.pack $ map (toEnum.fromEnum) v+ runtest :: ByteString -> String+ runtest v = runhash hash v - runtestinc i v = runhashinc hash $ splitB i $ B.pack $ map (toEnum.fromEnum) v+ runtestinc :: Int -> ByteString -> String+ runtestinc i v = runhashinc hash $ splitB i v maketest (i, v, r) =- [ testname i ~: r ~=? (runtest v),- testname i ~: r ~=? (runtestinc 1 v),- testname i ~: r ~=? (runtestinc 2 v),- testname i ~: r ~=? (runtestinc 3 v),- testname i ~: r ~=? (runtestinc 4 v),- testname i ~: r ~=? (runtestinc 5 v),- testname i ~: r ~=? (runtestinc 9 v),- testname i ~: r ~=? (runtestinc 16 v) ]+ [ testCase (show i ++ " one-pass") (r @=? runtest v)+ , testCase (show i ++ " inc 1") (r @=? runtestinc 1 v)+ , testCase (show i ++ " inc 2") (r @=? runtestinc 2 v)+ , testCase (show i ++ " inc 3") (r @=? runtestinc 3 v)+ , testCase (show i ++ " inc 4") (r @=? runtestinc 4 v)+ , testCase (show i ++ " inc 5") (r @=? runtestinc 5 v)+ , testCase (show i ++ " inc 9") (r @=? runtestinc 9 v)+ , testCase (show i ++ " inc 16") (r @=? runtestinc 16 v)+ ] -mapTests :: [Test]-mapTests = concatMap makeTestAlg results+katTests :: [Test]+katTests = map makeTestAlg results apiTests :: [Test] apiTests =- [ "sha1 api" ~: runhash sha1Hash B.empty ~=? show (hash B.empty :: Digest SHA1)- , "sha256 api" ~: runhash sha256Hash B.empty ~=? show (hash B.empty :: Digest SHA256)- , "sha512 api" ~: runhash sha512Hash B.empty ~=? show (hash B.empty :: Digest SHA512)- , "sha3-224 api" ~: runhash (sha3Hash 224) B.empty ~=? show (hash B.empty :: Digest SHA3_224)- , "sha3-256 api" ~: runhash (sha3Hash 256) B.empty ~=? show (hash B.empty :: Digest SHA3_256)- , "sha3-512 api" ~: runhash (sha3Hash 512) B.empty ~=? show (hash B.empty :: Digest SHA3_512)+ [ testCase "sha1 api" (runhash sha1Hash B.empty @=? show (hash B.empty :: Digest SHA1))+ , testCase "sha256 api" (runhash sha256Hash B.empty @=? show (hash B.empty :: Digest SHA256))+ , testCase "sha512 api" (runhash sha512Hash B.empty @=? show (hash B.empty :: Digest SHA512))+ , testCase "sha3-224 api" (runhash (sha3Hash 224) B.empty @=? show (hash B.empty :: Digest SHA3_224))+ , testCase "sha3-256 api" (runhash (sha3Hash 256) B.empty @=? show (hash B.empty :: Digest SHA3_256))+ , testCase "sha3-512 api" (runhash (sha3Hash 512) B.empty @=? show (hash B.empty :: Digest SHA3_512)) ] -tests = TestList (mapTests ++ apiTests) -main = runTestTT tests+data MACVector = MACVector { macKey :: ByteString+ , macSecret :: ByteString+ , macResult :: ByteString+ }++md5MACVectors =+ [ MACVector B.empty B.empty "\x74\xe6\xf7\x29\x8a\x9c\x2d\x16\x89\x35\xf5\x8c\x00\x1b\xad\x88"+ , MACVector "key" v1 "\x80\x07\x07\x13\x46\x3e\x77\x49\xb9\x0c\x2d\xc2\x49\x11\xe2\x75"+ ]++sha1MACVectors =+ [ MACVector B.empty B.empty "\xfb\xdb\x1d\x1b\x18\xaa\x6c\x08\x32\x4b\x7d\x64\xb7\x1f\xb7\x63\x70\x69\x0e\x1d"+ , MACVector "key" v1 "\xde\x7c\x9b\x85\xb8\xb7\x8a\xa6\xbc\x8a\x7a\x36\xf7\x0a\x90\x70\x1c\x9d\xb4\xd9"+ ]++sha256MACVectors =+ [ MACVector B.empty B.empty "\xb6\x13\x67\x9a\x08\x14\xd9\xec\x77\x2f\x95\xd7\x78\xc3\x5f\xc5\xff\x16\x97\xc4\x93\x71\x56\x53\xc6\xc7\x12\x14\x42\x92\xc5\xad"+ , MACVector "key" v1 "\xf7\xbc\x83\xf4\x30\x53\x84\x24\xb1\x32\x98\xe6\xaa\x6f\xb1\x43\xef\x4d\x59\xa1\x49\x46\x17\x59\x97\x47\x9d\xbc\x2d\x1a\x3c\xd8"+ ]++macTests :: [Test]+macTests =+ [ testGroup "hmac-md5" $ map (toMACTest MD5.hash 64) $ zip [0..] md5MACVectors+ , testGroup "hmac-sha1" $ map (toMACTest SHA1.hash 64) $ zip [0..] sha1MACVectors+ , testGroup "hmac-sha256" $ map (toMACTest SHA256.hash 64) $ zip [0..] sha256MACVectors+ ]+ where toMACTest hashF blockSize (i, macVector) =+ testCase (show i) (macResult macVector @=? HMAC.hmac hashF blockSize (macKey macVector) (macSecret macVector))++main = defaultMain+ [ testGroup "KATs" katTests+ , testGroup "API" apiTests+ , testGroup "MACs" macTests+ ]
cryptohash.cabal view
@@ -1,5 +1,5 @@ Name: cryptohash-Version: 0.8.2+Version: 0.8.3 Description: A collection of crypto hashes, with a practical incremental and one-pass, pure APIs, with performance close to the fastest implementations available in others languages.