packages feed

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 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")+    ])