saltine 0.0.0.2 → 0.0.0.3
raw patch · 11 files changed
+410/−5 lines, 11 filesdep ~base
Dependency ranges changed: base
Files
- saltine.cabal +12/−3
- src/Crypto/Saltine/Core/ScalarMult.hs +1/−1
- tests/AuthProperties.hs +22/−0
- tests/BoxProperties.hs +116/−0
- tests/Main.hs +3/−1
- tests/OneTimeAuthProperties.hs +22/−0
- tests/ScalarMultProperties.hs +76/−0
- tests/SecretBoxProperties.hs +60/−0
- tests/SignProperties.hs +33/−0
- tests/StreamProperties.hs +36/−0
- tests/Util.hs +29/−0
saltine.cabal view
@@ -1,5 +1,5 @@ name: saltine-version: 0.0.0.2+version: 0.0.0.3 synopsis: Cryptography that's easy to digest (NaCl/libsodium bindings). description: @@ -54,18 +54,27 @@ ghc-options: -Wall -funbox-strict-fields default-language: Haskell2010 build-depends: - base >= 4.5 && < 4.7,+ base >= 4.5 && < 4.8, bytestring, profunctors test-suite tests type: exitcode-stdio-1.0 main-is: Main.hs+ other-modules:+ AuthProperties+ BoxProperties+ OneTimeAuthProperties+ ScalarMultProperties+ SecretBoxProperties+ SignProperties+ StreamProperties+ Util ghc-options: -Wall -threaded -O0 -rtsopts hs-source-dirs: tests default-language: Haskell2010 build-depends:- base >= 4.5 && < 4.7,+ base >= 4.5 && < 4.8, saltine, bytestring, vector,
src/Crypto/Saltine/Core/ScalarMult.hs view
@@ -108,7 +108,7 @@ -> IO CInt -- ^ Always 0 -foreign import ccall "crypto_scalarmult"+foreign import ccall "crypto_scalarmult_base" c_scalarmult_base :: Ptr CChar -- ^ Output group element buffer -> Ptr CChar
+ tests/AuthProperties.hs view
@@ -0,0 +1,22 @@+{-# LANGUAGE OverloadedStrings #-}++module AuthProperties (+ testAuth+ ) where++import Util++import Crypto.Saltine.Core.Auth++import Test.Framework.Providers.QuickCheck2+import Test.Framework++testAuth :: Test+testAuth = buildTest $ do+ k <- newKey+ return $ testGroup "...Internal.Auth" [++ testProperty "Authenticates message"+ $ \(Message bs) -> verify k (auth k bs) bs == True++ ]
+ tests/BoxProperties.hs view
@@ -0,0 +1,116 @@+{-# LANGUAGE OverloadedStrings #-}++module BoxProperties (+ testBox+ ) where++import Util++import Crypto.Saltine.Core.Box++import qualified Data.ByteString as S++import Test.Framework.Providers.QuickCheck2+import Test.Framework++import Test.QuickCheck.Property+import Test.QuickCheck.Monadic++-- | Ciphertext can be decrypted+rightInverseProp :: Keypair -> Keypair -> Nonce -> Message -> Bool+rightInverseProp (sk1, pk1) (sk2, pk2) n (Message bs) =+ Just bs == boxOpen pk1 sk2 n (box pk2 sk1 n bs)++-- | Cannot decrypt without the corrent secret key+rightInverseFailureProp1 :: Keypair -> Keypair -> Nonce -> Message -> Bool+rightInverseFailureProp1 (sk1, pk1) (sk2, pk2) n (Message bs) =+ Nothing == boxOpen pk1 (perturb sk2) n (box pk2 sk1 n bs)++-- | Cannot decrypt when not sent to you+rightInverseFailureProp2 :: Keypair -> Keypair -> Nonce -> Message -> Bool+rightInverseFailureProp2 (sk1, pk1) (sk2, pk2) n (Message bs) =+ Nothing == boxOpen pk1 sk2 n (box (perturb pk2) sk1 n bs)++-- | Ciphertext cannot be decrypted (verification failure) if the+-- ciphertext is perturbed+rightInverseFailureProp3 :: Keypair -> Keypair -> Nonce -> Message -> Bool+rightInverseFailureProp3 (sk1, pk1) (sk2, pk2) n (Message bs) =+ Nothing == boxOpen pk1 sk2 n (S.reverse $ box pk2 sk1 n bs)++-- | Ciphertext cannot be decrypted with a different nonce+cannotDecryptNonceProp+ :: Keypair -> Keypair -> Nonce -> Nonce -> Message -> Bool+cannotDecryptNonceProp (sk1, pk1) (sk2, pk2) n1 n2 (Message bs) =+ Nothing == boxOpen pk1 sk2 n2 (box pk2 sk1 n1 bs)++-- | BeforeNM creates identical secret keys when called in an+-- anti-symmetric fashion.+beforeNMCreateSecretKeyProp :: Test.QuickCheck.Property.Property+beforeNMCreateSecretKeyProp = monadicIO . (assert =<<) . run $ do+ (sk1, pk1) <- newKeypair+ (sk2, pk2) <- newKeypair+ let ck_1for2 = beforeNM sk1 pk2+ ck_2for1 = beforeNM sk2 pk1+ return (ck_1for2 == ck_2for1)++-- | Ciphertext can be decrypted using combined keys+rightInverseAfterNMProp+ :: CombinedKey -> CombinedKey -> Nonce -> Message -> Bool+rightInverseAfterNMProp ck_1for2 ck_2for1 n (Message bs) =+ Just bs == boxOpenAfterNM ck_2for1 n (boxAfterNM ck_1for2 n bs)++-- | Perturbed ciphertext cannot be decrypted using combined keys+rightInverseFailureAfterNMProp1+ :: CombinedKey -> CombinedKey -> Nonce -> Message -> Bool+rightInverseFailureAfterNMProp1 ck_1for2 ck_2for1 n (Message bs) =+ Nothing == boxOpenAfterNM ck_2for1 n (S.reverse $ boxAfterNM ck_1for2 n bs)++testBox :: Test+testBox = buildTest $ do++ (sk1, pk1) <- newKeypair+ (sk2, pk2) <- newKeypair+ let ck_1for2 = beforeNM sk1 pk2+ ck_2for1 = beforeNM sk2 pk1+ n1 <- newNonce+ n2 <- newNonce++ return $ testGroup "...Internal.Box" [++ testGroup "Can decrypt ciphertext using..." [+ + testProperty "... public key/secret key"+ $ rightInverseProp (sk1, pk1) (sk2, pk2) n1 ,+ + testProperty "... combined key"+ $ rightInverseAfterNMProp ck_1for2 ck_2for1 n1+ + ],++ testGroup "Fail to verify ciphertext when..." [+ + testProperty "... not using proper secret key"+ $ rightInverseFailureProp1 (sk1, pk1) (sk2, pk2) n1,+ + testProperty "... not actually sent to you"+ $ rightInverseFailureProp2 (sk1, pk1) (sk2, pk2) n1,+ + testProperty "... ciphertext has been perturbed"+ $ rightInverseFailureProp3 (sk1, pk1) (sk2, pk2) n1,+ + testProperty "... using the wrong nonce"+ $ cannotDecryptNonceProp (sk1, pk1) (sk2, pk2) n1 n2,+ + testProperty "... using the wrong combined key"+ $ rightInverseFailureAfterNMProp1 ck_1for2 ck_2for1 n1+ + ],++ testGroup "(properties)" [++ testProperty "beforeNM is anti-symmetric" beforeNMCreateSecretKeyProp+ + ]+ ]+ +
tests/Main.hs view
@@ -8,6 +8,7 @@ import AuthProperties (testAuth) import OneTimeAuthProperties (testOneTimeAuth) import SignProperties (testSign)+import ScalarMultProperties (testScalarMult) import Test.Framework @@ -18,5 +19,6 @@ testStream, testAuth, testOneTimeAuth,- testSign+ testSign,+ testScalarMult ]
+ tests/OneTimeAuthProperties.hs view
@@ -0,0 +1,22 @@+{-# LANGUAGE OverloadedStrings #-}++module OneTimeAuthProperties (+ testOneTimeAuth+ ) where++import Util++import Crypto.Saltine.Core.OneTimeAuth++import Test.Framework.Providers.QuickCheck2+import Test.Framework++testOneTimeAuth :: Test+testOneTimeAuth = buildTest $ do+ k <- newKey+ return $ testGroup "...Internal.Auth" [++ testProperty "Authenticates message"+ $ \(Message bs) -> verify k (auth k bs) bs == True++ ]
+ tests/ScalarMultProperties.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE OverloadedStrings #-}++module ScalarMultProperties (+ testScalarMult+ ) where++import Util++import Crypto.Saltine.Class+import Crypto.Saltine.Core.ScalarMult++import qualified Data.ByteString as S+import Data.Maybe (fromJust)++import Test.Framework.Providers.QuickCheck2+import Test.Framework++-- Test vectors extracted from "Cryptography in NaCl",+-- http://cr.yp.to/highspeed/naclcrypto-20090310.pdf+alicesk, bobsk :: Scalar+alicesk = fromJust . decode $ S.pack+ [0x77,0x07,0x6d,0x0a,0x73,0x18,0xa5,0x7d+ ,0x3c,0x16,0xc1,0x72,0x51,0xb2,0x66,0x45+ ,0xdf,0x4c,0x2f,0x87,0xeb,0xc0,0x99,0x2a+ ,0xb1,0x77,0xfb,0xa5,0x1d,0xb9,0x2c,0x2a]+bobsk = fromJust . decode $ S.pack+ [0x5d,0xab,0x08,0x7e,0x62,0x4a,0x8a,0x4b+ ,0x79,0xe1,0x7f,0x8b,0x83,0x80,0x0e,0xe6+ ,0x6f,0x3b,0xb1,0x29,0x26,0x18,0xb6,0xfd+ ,0x1c,0x2f,0x8b,0x27,0xff,0x88,0xe0,0xeb]++alicepk, bobpk, sharedsk :: GroupElement+alicepk = fromJust . decode $ S.pack+ [0x85,0x20,0xf0,0x09,0x89,0x30,0xa7,0x54+ ,0x74,0x8b,0x7d,0xdc,0xb4,0x3e,0xf7,0x5a+ ,0x0d,0xbf,0x3a,0x0d,0x26,0x38,0x1a,0xf4+ ,0xeb,0xa4,0xa9,0x8e,0xaa,0x9b,0x4e,0x6a]+bobpk = fromJust . decode $ S.pack+ [0xde,0x9e,0xdb,0x7d,0x7b,0x7d,0xc1,0xb4+ ,0xd3,0x5b,0x61,0xc2,0xec,0xe4,0x35,0x37+ ,0x3f,0x83,0x43,0xc8,0x5b,0x78,0x67,0x4d+ ,0xad,0xfc,0x7e,0x14,0x6f,0x88,0x2b,0x4f]+sharedsk = fromJust . decode $ S.pack+ [0x4a,0x5d,0x9d,0x5b,0xa4,0xce,0x2d,0xe1+ ,0x72,0x8e,0x3b,0xf4,0x80,0x35,0x0f,0x25+ ,0xe0,0x7e,0x21,0xc9,0x47,0xd1,0x9e,0x33+ ,0x76,0xf0,0x9b,0x3c,0x1e,0x16,0x17,0x42]++testScalarMult :: Test+testScalarMult = buildTest $ do+ return $ testGroup "...Internal.ScalarMult" [++ testProperty "mult a (multBase a) /= multBase a"+ $ \(ByteString32 a') ->+ let Just a = decode a'+ in mult a (multBase a) /= multBase a,++ testProperty "mult a (multBase b) == mult b (multBase a)"+ $ \(ByteString32 a') (ByteString32 b') ->+ let Just a = decode a'+ Just b = decode b'+ in mult a (multBase b) == mult b (multBase a),++ testProperty "matches test vector for alice"+ $ multBase alicesk == alicepk,++ testProperty "matches test vector for bob"+ $ multBase bobsk == bobpk,++ testProperty "matches test vector for shared secret from alice's view"+ $ mult alicesk bobpk == sharedsk,++ testProperty "matches test vector for shared secret from bob's view"+ $ mult bobsk alicepk == sharedsk++ ]
+ tests/SecretBoxProperties.hs view
@@ -0,0 +1,60 @@+{-# LANGUAGE OverloadedStrings #-}++module SecretBoxProperties (+ testSecretBox+ ) where++import Util++import Crypto.Saltine.Core.SecretBox++import qualified Data.ByteString as S++import Test.Framework.Providers.QuickCheck2+import Test.Framework++-- | Ciphertext can be decrypted+rightInverseProp :: Key -> Nonce -> Message -> Bool+rightInverseProp k n (Message bs) =+ Just bs == secretboxOpen k n (secretbox k n bs)++-- | Ciphertext cannot be decrypted if the ciphertext is perturbed+rightInverseFailureProp :: Key -> Nonce -> Message -> Bool+rightInverseFailureProp k n (Message bs) =+ Nothing == secretboxOpen k n (S.reverse $ secretbox k n bs)++-- | Ciphertext cannot be decrypted with a different key+cannotDecryptKeyProp :: Key -> Key -> Nonce -> Message -> Bool+cannotDecryptKeyProp k1 k2 n (Message bs) =+ Nothing == secretboxOpen k2 n (secretbox k1 n bs)++-- | Ciphertext cannot be decrypted with a different nonce+cannotDecryptNonceProp :: Key -> Nonce -> Nonce -> Message -> Bool+cannotDecryptNonceProp k n1 n2 (Message bs) =+ Nothing == secretboxOpen k n2 (secretbox k n1 bs)++testSecretBox :: Test+testSecretBox = buildTest $ do+ k1 <- newKey+ k2 <- newKey+ n1 <- newNonce+ n2 <- newNonce++ return $ testGroup "...Internal.SecretBox" [++ testProperty "Can decrypt ciphertext"+ $ rightInverseProp k1 n1,++ testGroup "Cannot decrypt ciphertext when..." [++ testProperty "... ciphertext is perturbed"+ $ rightInverseFailureProp k1 n1,++ testProperty "... using the wrong key"+ $ cannotDecryptKeyProp k1 k2 n1,++ testProperty "... using the wrong nonce"+ $ cannotDecryptNonceProp k1 n1 n2+ + ]+ ]
+ tests/SignProperties.hs view
@@ -0,0 +1,33 @@+{-# LANGUAGE OverloadedStrings #-}++module SignProperties (+ testSign+ ) where++import Util++import Crypto.Saltine.Core.Sign++import qualified Data.ByteString as S++import Test.Framework.Providers.QuickCheck2+import Test.Framework+import Test.QuickCheck++testSign :: Test+testSign = buildTest $ do+ (sk1, pk1) <- newKeypair+ (_sk2, pk2) <- newKeypair++ return $ testGroup "...Internal.Sign" [++ testProperty "Verifies signed message"+ $ \(Message bs) -> signOpen pk1 (sign sk1 bs) == Just bs,++ testProperty "Signed message longer than message"+ $ \(Message bs) -> S.length (sign sk1 bs) >= S.length bs,++ testProperty "Rejects message with mismatched key"+ $ \(Message bs) -> (not $ S.null bs) ==> signOpen pk2 (sign sk1 bs) == Nothing++ ]
+ tests/StreamProperties.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE OverloadedStrings #-}++module StreamProperties (+ testStream+ ) where++import Util++import Crypto.Saltine.Core.Stream++import qualified Data.ByteString as S++import Test.Framework.Providers.QuickCheck2+import Test.Framework+import Test.QuickCheck++testStream :: Test+testStream = buildTest $ do+ k <- newKey+ n <- newNonce+ return $ testGroup "...Internal.Stream" [++ testProperty "Stream is apropriately sized"+ $ \len -> (len > 0 && len < 200)+ ==> S.length (stream k n len) == len,++ testProperty "xor munges input"+ $ \(Message bs) -> not (S.null bs)+ ==> xor k n bs /= bs,++ testProperty "xor is involutive"+ $ \(Message bs) -> xor k n (xor k n bs) == bs+ + ]+ +
+ tests/Util.hs view
@@ -0,0 +1,29 @@+module Util where++import Test.QuickCheck++import Crypto.Saltine.Class++import qualified Data.ByteString as S+import qualified Data.ByteString.Char8 as S8+import Control.Monad (replicateM)++import Control.Applicative++perturb :: IsEncoding a => a -> a+perturb a = case decode (S.reverse (encode a)) of+ Just x -> x+ Nothing -> error "Util.perturb"++newtype ByteString32 = ByteString32 S.ByteString deriving (Show)++instance Arbitrary ByteString32 where+ arbitrary = ByteString32 . S.pack <$> replicateM 32 arbitrary++newtype Message = Message S.ByteString deriving (Show)++instance Arbitrary Message where+ arbitrary = Message . S.intercalate (S8.pack " ") <$> listOf (oneof [+ return (S8.pack "word"),+ return (S8.pack "other word")+ ])