{-# LANGUAGE OverloadedStrings #-} import Test.HUnit ((~:), (~=?)) import qualified Test.HUnit as Unit import Test.QuickCheck import Test.QuickCheck.Test import System.IO (hFlush, stdout) import Control.Monad import Data.List (intercalate) import Data.Char import Data.Bits import Data.Word import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as BC -- numbers import Number.ModArithmetic -- ciphers import qualified Crypto.Cipher.RC4 as RC4 import qualified Crypto.Cipher.Camellia as Camellia import qualified Crypto.Cipher.RSA as RSA import Crypto.Random encryptStream fi fc key plaintext = B.unpack $ snd $ fc (fi key) plaintext encryptBlock fi fc key plaintext = let e = fi key in case e of Right k -> B.unpack $ fc k plaintext Left e -> error e wordify :: [Char] -> [Word8] wordify = map (toEnum . fromEnum) vectors_rc4 = [ (wordify "Key", "Plaintext", [ 0xBB,0xF3,0x16,0xE8,0xD9,0x40,0xAF,0x0A,0xD3 ]) , (wordify "Wiki", "pedia", [ 0x10,0x21,0xBF,0x04,0x20 ]) , (wordify "Secret", "Attack at dawn", [ 0x45,0xA0,0x1F,0x64,0x5F,0xC3,0x5B,0x38,0x35,0x52,0x54,0x4B,0x9B,0xF5 ]) ] vectors_camellia128 = [ ( [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] , B.pack [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] , [0x3d,0x02,0x80,0x25,0xb1,0x56,0x32,0x7c,0x17,0xf7,0x62,0xc1,0xf2,0xcb,0xca,0x71] ) , ( [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10] , B.pack [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10] , [0x67,0x67,0x31,0x38,0x54,0x96,0x69,0x73,0x08,0x57,0x06,0x56,0x48,0xea,0xbe,0x43] ) ] vectors_camellia192 = [ ( [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77] , B.pack [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10] ,[0xb4,0x99,0x34,0x01,0xb3,0xe9,0x96,0xf8,0x4e,0xe5,0xce,0xe7,0xd7,0x9b,0x09,0xb9] ) ] vectors_camellia256 = [ ( [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10 ,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff] , B.pack [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10] , [0x9a,0xcc,0x23,0x7d,0xff,0x16,0xd7,0x6c,0x20,0xef,0x7c,0x91,0x9e,0x3a,0x75,0x09] ) ] vectors = [ ("RC4", vectors_rc4, encryptStream RC4.initCtx RC4.encrypt) , ("Camellia", vectors_camellia128, encryptBlock Camellia.initKey Camellia.encrypt) ] utests :: [Unit.Test] utests = concatMap (\(name, v, f) -> map (\(k,p,e) -> name ~: name ~: e ~=? f k p) v) vectors {- end of units tests -} {- start of QuickCheck verification -} -- FIXME better to tweak the property to generate positive integer instead of this. prop_gcde_binary_valid (a, b) | a > 0 && b >= 0 = let (x,y,v) = gcde_binary a b in and [a*x + b*y == v, gcd a b == v] | otherwise = True prop_modexp_rtl_valid (a, b, m) | m > 0 && a >= 0 && b >= 0 = exponantiation_rtl_binary a b m == ((a ^ b) `mod` m) | otherwise = True prop_modinv_valid (a, m) | m > 1 && a > 0 = case inverse a m of Just ainv -> (ainv * a) `mod` m == 1 Nothing -> True | otherwise = True newtype RSAMessage = RSAMessage B.ByteString deriving (Show, Eq) instance Arbitrary RSAMessage where arbitrary = do sz <- choose (0, 128 - 11) ws <- replicateM sz (choose (0,255) :: Gen Int) return $ RSAMessage $ B.pack $ map fromIntegral ws data Rng = Rng Int instance CryptoRandomGen Rng where newGen _ = Right (Rng 0) genSeedLength = 0 genBytes len g = Right (B.pack $ replicate len 0x2d, g) rng = Rng 0 prop_rsa_fast_valid (RSAMessage msg) = (either Left (RSA.decrypt privatekey . fst) $ RSA.encrypt rng publickey msg) == Right msg prop_rsa_slow_valid (RSAMessage msg) = (either Left (RSA.decrypt pk . fst) $ RSA.encrypt rng publickey msg) == Right msg where pk = privatekey { RSA.private_p = 0, RSA.private_q = 0 } privatekey = RSA.PrivateKey { RSA.private_sz = 128 , RSA.private_n = 140203425894164333410594309212077886844966070748523642084363106504571537866632850620326769291612455847330220940078873180639537021888802572151020701352955762744921926221566899281852945861389488419179600933178716009889963150132778947506523961974222282461654256451508762805133855866018054403911588630700228345151 , RSA.private_d = 133764127300370985476360382258931504810339098611363623122953018301285450176037234703101635770582297431466449863745848961134143024057267778947569638425565153896020107107895924597628599677345887446144410702679470631826418774397895304952287674790343620803686034122942606764275835668353720152078674967983573326257 , RSA.private_p = 12909745499610419492560645699977670082358944785082915010582495768046269235061708286800087976003942261296869875915181420265794156699308840835123749375331319 , RSA.private_q = 10860278066550210927914375228722265675263011756304443428318337179619069537063135098400347475029673115805419186390580990519363257108008103841271008948795129 , RSA.private_dP = 5014229697614831746694710412330921341325464081424013940131184365711243776469716106024020620858146547161326009604054855316321928968077674343623831428796843 , RSA.private_dQ = 3095337504083058271243917403868092841421453478127022884745383831699720766632624326762288333095492075165622853999872779070009098364595318242383709601515849 , RSA.private_qinv = 11136639099661288633118187183300604127717437440459572124866697429021958115062007251843236337586667012492941414990095176435990146486852255802952814505784196 } publickey = RSA.PublicKey { RSA.public_sz = 128 , RSA.public_n = 140203425894164333410594309212077886844966070748523642084363106504571537866632850620326769291612455847330220940078873180639537021888802572151020701352955762744921926221566899281852945861389488419179600933178716009889963150132778947506523961974222282461654256451508762805133855866018054403911588630700228345151 , RSA.public_e = 65537 } args = Args { replay = Nothing , maxSuccess = 1000 , maxDiscard = 4000 , maxSize = 1000 } run_test n t = putStr (" " ++ n ++ " ... ") >> hFlush stdout >> quickCheckWith args t main = do Unit.runTestTT (Unit.TestList utests) run_test "gcde binary valid" prop_gcde_binary_valid run_test "exponantiation RTL valid" prop_modexp_rtl_valid run_test "inverse valid" prop_modinv_valid run_test "RSA decrypt(slow).encrypt = id" prop_rsa_slow_valid run_test "RSA decrypt(fast).encrypt = id" prop_rsa_fast_valid