-- from base import Control.Applicative ((<$>)) import Control.Monad (forM_, unless) import Data.Char (isNumber) import Data.Maybe (catMaybes) import Text.Printf -- from bytestring import qualified Data.ByteString as B --import qualified Data.ByteString.Lazy as L -- from cereal import Data.Serialize (encode) -- from tagged import Data.Tagged (Tagged(..)) -- from crypto-api import Crypto.Classes -- from filepath import System.FilePath (()) -- from transformers import Control.Monad.Trans.Writer.Lazy (Writer) -- from hspec import Test.Hspec.Monadic --import Test.Hspec.QuickCheck import Test.Hspec.HUnit () -- from this package import Paths_skein (getDataFileName) import Crypto.Skein main :: IO () main = do skein_golden_kat_txt <- getDataFileName ("tests" "skein_golden_kat.txt") kats <- parseKats <$> readFile skein_golden_kat_txt putStrLn $ "Parsed " ++ show (length kats) ++ " known answer tests" hspecX $ do describe "Skein golden known answer tests" $ do skeinKats kats (undefined :: Skein_512_512) skeinKats kats (undefined :: Skein_1024_1024) skeinKats kats (undefined :: Skein_256_256) skeinKats kats (undefined :: Skein_256_128) skeinKats kats (undefined :: Skein_256_160) skeinKats kats (undefined :: Skein_256_224) skeinKats kats (undefined :: Skein_512_128) skeinKats kats (undefined :: Skein_512_160) skeinKats kats (undefined :: Skein_512_224) skeinKats kats (undefined :: Skein_512_256) skeinKats kats (undefined :: Skein_512_384) skeinKats kats (undefined :: Skein_1024_384) skeinKats kats (undefined :: Skein_1024_512) readMsg :: Read a => String -> String -> a readMsg msg str = case readsPrec 0 str of [(r, "")] -> r _ -> error msg ---------------------------------------------------------------------- data Kat = Kat { skeinType :: SkeinType , message :: B.ByteString , macKey :: Maybe B.ByteString , result :: B.ByteString } data SkeinType = Skein !Int !Int deriving (Eq) instance Show SkeinType where show (Skein s o) = printf "Skein-%d-%d" s o parseKats :: String -> [Kat] parseKats = catMaybes . map parseKat . groupKats . lines . filter (/= '\r') groupKats :: [String] -> [[String]] groupKats = go [] where sep = "--------------------------------" go acc (x:xs) | x == sep = reverse acc : go [] xs | otherwise = go (x:acc) xs go [] [] = [] go (_:_) [] = error "groupKats: didn't find last separator" parseKat :: [String] -> Maybe Kat parseKat ("":xs) = parseKat xs parseKat (header:"":rest) = case (isTree header, parseMsgLen header, parseBlocks rest) of (_, msgLen, _) | msgLen `mod` 8 /= 0 -> Nothing (False, _, [Message msg, Result ret]) -> kat msg Nothing ret (False, _, [Message msg, MACKey mac, Result ret]) -> kat msg (Just mac) ret _ -> Nothing where kat msg mac ret = Just $ Kat (parseSkeinType header) msg mac ret parseKat _ = Nothing isTree :: String -> Bool isTree ('T':'r':'e':'e':':':_) = True isTree (_:xs) = isTree xs isTree [] = False parseMsgLen :: String -> Int parseMsgLen ('m':'s':'g':'L':'e':'n':' ':'=':xs) = readMsg "parseMsgLen" $ take 6 xs parseMsgLen (_:xs) = parseMsgLen xs parseMsgLen [] = error "parseMsgLen: didn't find msgLen" parseSkeinType :: String -> SkeinType parseSkeinType xs0 = let (":Skein", '-':xs1) = break (== '-') xs0 (stateS, xs2) = span isNumber xs1 (':':_, xs3) = break isNumber xs2 (outputS, _) = span isNumber xs3 in Skein (readMsg "stateS" stateS) (readMsg "outputS" outputS) data Block = Message B.ByteString | MACKey B.ByteString | Result B.ByteString block :: String -> B.ByteString -> Block block "Message data:" = Message block "Result:" = Result block ('M':'A':'C':_) = MACKey block x = error $ "block: unknown block type " ++ x parseBlocks :: [String] -> [Block] parseBlocks [] = [] parseBlocks (header:rest) | last header /= ':' = error "parseBlocks: something went wrong" | otherwise = let (data_, rest') = span ((== ' ') . head) rest in block header (parseData data_) : parseBlocks rest' parseData :: [String] -> B.ByteString parseData [' ':' ':' ':' ':'(':'n':'o':'n':'e':')':_] = B.empty parseData xs = B.pack $ map (readMsg "parseData" . ("0x"++)) $ concatMap words xs ---------------------------------------------------------------------- skeinKats :: (SkeinMAC skeinCtx, Hash skeinCtx digest) => [Kat] -> digest -> Writer [Spec] () skeinKats kats digest = let get t@(Tagged x) = x where f :: Tagged d a -> d f = undefined p = f t `asTypeOf` digest skeinType = Skein (get blockLength) (get outputLength) myHashKats = [(msg, ret) | Kat t msg Nothing ret <- kats, t == skeinType] myMacKats = [(msg, macKey, ret) | Kat t msg (Just macKey) ret <- kats, t == skeinType] lenHashKats = length myHashKats lenMacKats = length myMacKats testName = if lenHashKats + lenMacKats == 0 then printf "has no tests for %s =(" (show skeinType) else printf "works for %s (%d hash tests, %d MAC tests)" (show skeinType) lenHashKats lenMacKats in it testName $ do putStrLn "Testing hashes..." forM_ myHashKats $ \(msg, ret) -> do let myHash = hash' msg `asTypeOf` digest unless (encode myHash == ret) $ fail $ concat ["Message: ", show msg, "\nExpected: ", show ret, "\nCalculated: ", show (encode myHash)] putStrLn "Testing MACs..." forM_ myMacKats $ \(msg, macKey, ret) -> do let myMAC = skeinMAC' macKey msg `asTypeOf` digest unless (encode myMAC == ret) $ fail $ concat ["Message: ", show msg, "MAC Key: ", show macKey, "\nExpected: ", show ret, "\nCalculated: ", show (encode myMAC)]