{-# LANGUAGE EmptyDataDecls, MultiParamTypeClasses #-} import qualified Crypto.Random.DRBG.Hash as H import qualified Crypto.Random.DRBG.HMAC as M import Crypto.Random.DRBG import Crypto.Hash.SHA1 import Crypto.Hash.SHA224 import Crypto.Hash.SHA256 import Crypto.Hash.SHA384 import Crypto.Hash.SHA512 import qualified Data.ByteString as B import Crypto.Classes import Data.Serialize as Ser import Data.Serialize.Put as S import Data.Binary as Bin import Data.Binary.Put as P import Text.PrettyPrint.HughesPJClass import qualified Data.ByteString.Lazy.Char8 as L import qualified Data.ByteString.Lazy as LN import Data.Bits (shiftR, shiftL) import Crypto.HMAC import Crypto.Types import Data.Bits (xor) import Data.Tagged import Data.Maybe (maybeToList) import Data.List (deleteBy, isPrefixOf) import Test.Crypto import Test.ParseNistKATs import Paths_DRBG main = hmacMain >> hashMain -- Test the SHA-256 HMACs (other hash implementations will be tested once crypthash uses the crypto-api classes) nistTests_HMAC :: IO [Test] nistTests_HMAC = do contents <- getDataFileName "Test/HMAC_DRBG.txt" >>= readFile let cats = parseCategories "COUNT" contents return (concat $ concatMap (maybeToList . categoryToTest_HMAC) cats) -- Currently run SHA-256 tests only categoryToTest_HMAC :: TestCategory -> Maybe [Test] categoryToTest_HMAC (props, ts) = let p = case shaNumber props of Just 1 -> let p = Proxy :: Proxy SHA1 in build p Just 224 -> let p = Proxy :: Proxy SHA224 in build p Just 256 -> let p = Proxy :: Proxy SHA256 in build p Just 384 -> let p = Proxy :: Proxy SHA384 in build p Just 512 -> let p = Proxy :: Proxy SHA512 in build p _ -> error $ "Unrecognized Hash when building HMAC tests" ++ (show props) in case p of Nothing -> Nothing Just b -> let s = unlines $ map showProp props h = hashFunc (undefined :: SHA256) tests = concatMap (maybeToList . b) ts in Just tests where deleteF k lst = deleteBy (const $ (==) k . fst) undefined lst isPR = Just True == fmap read (lookup "PredictionResistance" props) showProp (p,"") = '[' : p ++ "]" showProp (p,v) = '[' : p ++ " = " ++ v ++ "]" testName = fst (head props) ++ (if isPR then "_PR" else "") build :: Hash c s => Proxy s -> Maybe ([Record] -> Maybe Test) build = Just . buildKAT . proxyToHMACState -- buildKAT :: Proxy (M.State a) -> [Record] -> Maybe Test buildKAT p t | fmap read (lookup "PredictionResistance" props) == Just True = do cnt <- lookup "COUNT" t let name = testName ++ "-" ++ cnt eIn <- lookup "EntropyInput" t n <- lookup "Nonce" t per <- lookup "PersonalizationString" t aIn1 <- lookup "AdditionalInput" t eInPR1 <- lookup "EntropyInputPR" t let t' = deleteF "EntropyInputPR" (deleteF "AdditionalInput" t) aIn2 <- lookup "AdditionalInput" t' eInPR2 <- lookup "EntropyInputPR" t' ret <- lookup "ReturnedBits" t' let f = let olen = proxy outputLength (proxyUnwrapHMACState p) hx = hexStringToBS st0 = M.instantiate (hx eIn) (hx n) (hx per) st1 = M.reseed st0 (hx eInPR1) (hx aIn1) `asProxyTypeOf` p Just (_,st2) = M.generate st1 olen B.empty st3 = M.reseed st2 (hx eInPR2) (hx aIn2) Just (r1,_) = M.generate st3 olen B.empty in r1 return (TK (f == L.fromChunks [hexStringToBS ret]) name) | otherwise = do cnt <- lookup "COUNT" t let name = testName ++ "-" ++ cnt eIn <- lookup "EntropyInput" t n <- lookup "Nonce" t per <- lookup "PersonalizationString" t aIn1 <- lookup "AdditionalInput" t eInRS <- lookup "EntropyInputReseed" t aInRS <- lookup "AdditionalInputReseed" t let t' = deleteF "AdditionalInput" t aIn2 <- lookup "AdditionalInput" t' ret <- lookup "ReturnedBits" t let f = let olen = proxy outputLength (proxyUnwrapHMACState p) hx = hexStringToBS st0 = M.instantiate (hx eIn) (hx n) (hx per) `asProxyTypeOf` p Just (_,st1) = M.generate st0 olen (hx aIn1) st2 = M.reseed st1 (hx eInRS) (hx aInRS) Just (r1, _) = M.generate st2 olen (hx aIn2) in r1 return (TK (f == L.fromChunks [hexStringToBS ret]) name) -- Test the HMAC DRBG functionallity hmacMain = nistTests_HMAC >>= runTests hashMain = nistTests_Hash >>= runTests nistTests_Hash :: IO [Test] nistTests_Hash = do contents <- getDataFileName "Test/Hash_DRBG.txt" >>= readFile let cats = parseCategories "COUNT" contents return (concat $ concatMap (maybeToList . categoryToTest_Hash) cats) categoryToTest_Hash :: TestCategory -> Maybe [Test] categoryToTest_Hash (props, ts) = let p = case shaNumber props of Just 1 -> let p = Proxy :: Proxy SHA1 in build p Just 224 -> let p = Proxy :: Proxy SHA224 in build p Just 256 -> let p = Proxy :: Proxy SHA256 in build p Just 384 -> let p = Proxy :: Proxy SHA384 in build p Just 512 -> let p = Proxy :: Proxy SHA512 in build p _ -> error $ "Unrecognized hash when building Hash DRBG test" ++ (show props) in case p of Nothing -> Nothing Just b -> Just $ concatMap (maybeToList . b) ts where deleteF k lst = deleteBy (const $ (==) k . fst) undefined lst isPR = Just True == fmap read (lookup "PredictionResistance" props) testName = fst (head props) ++ (if isPR then "_PR" else "") build :: (Hash c s, H.SeedLength s) => Proxy s -> Maybe ([Record] -> Maybe Test) build = Just . buildKAT . proxyToHashState buildKAT p t | isPR = do cnt <- lookup "COUNT" t let name = testName ++ "-" ++ cnt eIn <- lookup "EntropyInput" t n <- lookup "Nonce" t per <- lookup "PersonalizationString" t aIn1 <- lookup "AdditionalInput" t eInPR1 <- lookup "EntropyInputPR" t let t' = deleteF "EntropyInputPR" (deleteF "AdditionalInput" t) aIn2 <- lookup "AdditionalInput" t' eInPR2 <- lookup "EntropyInputPR" t' ret <- lookup "ReturnedBits" t' let f = let olen = proxy outputLength (proxyUnwrapHashState p) hx = hexStringToBS st0 = H.instantiate (hx eIn) (hx n) (hx per) `asProxyTypeOf` p st1 = H.reseed st0 (hx eInPR1) (hx aIn1) Just (_,st2) = H.generate st1 olen B.empty st3 = H.reseed st2 (hx eInPR2) (hx aIn2) Just (r1,_) = H.generate st3 olen B.empty in r1 return (TK (f == L.fromChunks [hexStringToBS ret]) name) buildKAT p t | otherwise = do cnt <- lookup "COUNT" t let name = testName ++ "-" ++ cnt eIn <- lookup "EntropyInput" t n <- lookup "Nonce" t per <- lookup "PersonalizationString" t aIn1 <- lookup "AdditionalInput" t eInRS <- lookup "EntropyInputReseed" t aInRS <- lookup "AdditionalInputReseed" t let t' = deleteF "AdditionalInput" t aIn2 <- lookup "AdditionalInput" t' ret <- lookup "ReturnedBits" t let f = let olen = proxy outputLength (proxyUnwrapHashState p) hx = hexStringToBS st0 = H.instantiate (hx eIn) (hx n) (hx per) `asProxyTypeOf` p Just (_,st1) = H.generate st0 olen (hx aIn1) st2 = H.reseed st1 (hx eInRS) (hx aInRS) Just (r1, _) = H.generate st2 olen (hx aIn2) in r1 return (TK (f == L.fromChunks [hexStringToBS ret]) name) proxyUnwrapHashState :: Proxy (H.State a) -> Proxy a proxyUnwrapHashState = const Proxy proxyUnwrapHMACState :: Proxy (M.State a) -> Proxy a proxyUnwrapHMACState = const Proxy i2bs :: BitLength -> Integer -> B.ByteString i2bs l i = B.unfoldr (\l' -> if l' < 0 then Nothing else Just (fromIntegral (i `shiftR` l'), l' - 8)) (l-8) bs2i :: B.ByteString -> Integer bs2i bs = B.foldl' (\i b -> (i `shiftL` 8) + fromIntegral b) 0 bs proxyToHMACState :: Proxy a -> Proxy (M.State a) proxyToHMACState _ = Proxy proxyToHashState :: Proxy a -> Proxy (H.State a) proxyToHashState _ = Proxy shaNumber :: Properties -> Maybe Int shaNumber ps = case filter ("SHA-" `isPrefixOf`) (map fst ps) of [s] -> Just $ read (drop 4 s) [] -> Nothing