diff --git a/saltine.cabal b/saltine.cabal
--- a/saltine.cabal
+++ b/saltine.cabal
@@ -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,
diff --git a/src/Crypto/Saltine/Core/ScalarMult.hs b/src/Crypto/Saltine/Core/ScalarMult.hs
--- a/src/Crypto/Saltine/Core/ScalarMult.hs
+++ b/src/Crypto/Saltine/Core/ScalarMult.hs
@@ -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
diff --git a/tests/AuthProperties.hs b/tests/AuthProperties.hs
new file mode 100644
--- /dev/null
+++ b/tests/AuthProperties.hs
@@ -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
+
+    ]
diff --git a/tests/BoxProperties.hs b/tests/BoxProperties.hs
new file mode 100644
--- /dev/null
+++ b/tests/BoxProperties.hs
@@ -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
+      
+      ]
+    ]
+    
+       
diff --git a/tests/Main.hs b/tests/Main.hs
--- a/tests/Main.hs
+++ b/tests/Main.hs
@@ -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
   ]
diff --git a/tests/OneTimeAuthProperties.hs b/tests/OneTimeAuthProperties.hs
new file mode 100644
--- /dev/null
+++ b/tests/OneTimeAuthProperties.hs
@@ -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
+
+    ]
diff --git a/tests/ScalarMultProperties.hs b/tests/ScalarMultProperties.hs
new file mode 100644
--- /dev/null
+++ b/tests/ScalarMultProperties.hs
@@ -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
+
+    ]
diff --git a/tests/SecretBoxProperties.hs b/tests/SecretBoxProperties.hs
new file mode 100644
--- /dev/null
+++ b/tests/SecretBoxProperties.hs
@@ -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
+      
+      ]
+    ]
diff --git a/tests/SignProperties.hs b/tests/SignProperties.hs
new file mode 100644
--- /dev/null
+++ b/tests/SignProperties.hs
@@ -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
+
+    ]
diff --git a/tests/StreamProperties.hs b/tests/StreamProperties.hs
new file mode 100644
--- /dev/null
+++ b/tests/StreamProperties.hs
@@ -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
+    
+    ]
+    
+       
diff --git a/tests/Util.hs b/tests/Util.hs
new file mode 100644
--- /dev/null
+++ b/tests/Util.hs
@@ -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")
+    ])
