diff --git a/src/Crypto/RNCryptor/Types.hs b/src/Crypto/RNCryptor/Types.hs index 7e21eac..c1ddef4 100644 --- a/src/Crypto/RNCryptor/Types.hs +++ b/src/Crypto/RNCryptor/Types.hs @@ -2,6 +2,7 @@ module Crypto.RNCryptor.Types ( RNCryptorHeader(..) , RNCryptorContext(ctxHeader, ctxCipher) + , UserInput(..) , newRNCryptorContext , newRNCryptorHeader , renderRNCryptorHeader @@ -17,6 +18,7 @@ import Control.Applicative import Control.Monad import Crypto.Cipher.AES import Crypto.PBKDF.ByteString +import Test.QuickCheck data RNCryptorHeader = RNCryptorHeader { @@ -36,6 +38,25 @@ data RNCryptorHeader = RNCryptorHeader { -- as the HMAC is at the end of the file. } +instance Show RNCryptorHeader where + show = C8.unpack . renderRNCryptorHeader + +instance Arbitrary RNCryptorHeader where + arbitrary = do + let version = toEnum 3 + let options = toEnum 1 + eSalt <- C8.pack <$> vector saltSize + iv <- C8.pack <$> vector blockSize + hmacSalt <- C8.pack <$> vector saltSize + return RNCryptorHeader { + rncVersion = version + , rncOptions = options + , rncEncryptionSalt = eSalt + , rncHMACSalt = hmacSalt + , rncIV = iv + , rncHMAC = \uKey -> sha1PBKDF2 uKey hmacSalt 10000 32 + } + -------------------------------------------------------------------------------- saltSize :: Int saltSize = 8 @@ -82,6 +103,11 @@ data RNCryptorContext = RNCryptorContext { , ctxCipher :: AES } +newtype UserInput = UI { unInput :: ByteString } deriving Show + +instance Arbitrary UserInput where + arbitrary = UI . C8.pack <$> arbitrary + -------------------------------------------------------------------------------- newRNCryptorContext :: ByteString -> RNCryptorHeader -> RNCryptorContext newRNCryptorContext userKey hdr = diff --git a/src/Crypto/RNCryptor/V3.hs b/src/Crypto/RNCryptor/V3.hs index a4a2e2d..69f33c4 100644 --- a/src/Crypto/RNCryptor/V3.hs +++ b/src/Crypto/RNCryptor/V3.hs @@ -2,7 +2,9 @@ module Crypto.RNCryptor.V3 ( module Crypto.RNCryptor.V3.Encrypt , module Crypto.RNCryptor.V3.Decrypt + , module Crypto.RNCryptor.Types ) where import Crypto.RNCryptor.V3.Encrypt import Crypto.RNCryptor.V3.Decrypt +import Crypto.RNCryptor.Types diff --git a/test/Main.hs b/test/Main.hs index 09e12b5..a5e17f1 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -1,11 +1,8 @@ {-# LANGUAGE OverloadedStrings #-} module Main where -import System.Environment -import Data.Monoid import Tests import Test.Tasty -import Test.Tasty.HUnit import Test.Tasty.QuickCheck ---------------------------------------------------------------------- @@ -18,5 +15,7 @@ main :: IO () main = do defaultMainWithIngredients defaultIngredients $ testGroup "RNCryptor tests" $ [ - testGroup "RNCryptor properties" [] + withQuickCheckDepth "RNCryptor properties" 100 [ + testProperty "encrypt/decrypt roundtrip" testEncryptDecryptRoundtrip + ] ] diff --git a/test/Tests.hs b/test/Tests.hs index 8c4d87e..17c80fb 100644 --- a/test/Tests.hs +++ b/test/Tests.hs @@ -1,3 +1,23 @@ +{-# LANGUAGE ScopedTypeVariables #-} module Tests where -import Test.Tasty.HUnit +import Test.Tasty.QuickCheck +import Crypto.RNCryptor.V3 +import Control.Applicative +import qualified Data.ByteString as B + + +newtype TestVector = TV (UserInput, UserInput, RNCryptorHeader) deriving Show + +instance Arbitrary TestVector where + arbitrary = TV <$> ((,,) <$> arbitrary <*> arbitrary <*> arbitrary) + + +testEncryptDecryptRoundtrip :: Property +testEncryptDecryptRoundtrip = + forAll arbitrary $ \(TV (input,pwd,hdr)) -> + B.length (unInput input) > 0 && + B.length (unInput pwd) > 0 ==> + let ctx = newRNCryptorContext (unInput pwd) hdr + encrypted = encrypt ctx (unInput input) + in decrypt encrypted (unInput pwd) == unInput input diff --git "a/\177" "b/\177" new file mode 100644 index 0000000..e69de29