packages feed

saltine 0.1.0.1 → 0.2.2.0

raw patch · 66 files changed

Files

+ CHANGELOG.md view
@@ -0,0 +1,51 @@+# Changelog+All notable changes to this project will be documented in this file.++The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)++## [Unreleased]+### Added+### Changed++## [0.2.2.0] - 2025-02-22+### Added+- SHA256 support, thanks [@jmazon](https://github.com/jmazon)+- Ed25519 to Curve25519 conversion support, thanks [@jmazon](https://github.com/jmazon)+### Changed+- Version bounds relaxed++## [0.2.1.0] - 2023-02-17+### Changed+- Fix Show instances formatting, and add instances for Keypairs, thanks [@NicolasT](https://github.com/NicolasT)++## [0.2.0.1] - 2022-04-30+### Changed+- Relax version bounds on text and bytestring, thanks [@ysangkok](https://github.com/ysangkok)++## [0.2.0.0] - 2021-05-27+### Added+- All AEAD variants are now in saltine+- Key comparisons now use sodium_memcmp to prevent timing attacks+- Liberal use of Internal modules+- Benchmarks added+- Export Key/Nonce/… constructors from Internal module+- New password hashing module+- Show instances for most (all?) relevant data types+- Signature types for detached functions++### Changed+- newtype accessor functions added, keypairs are separate data types now instead+of tuples++## [0.1.1.1] - 2021-01-15+### Changed+- Fix for running tests in `cabal repl` (thanks [@timds])+- Allow newer profunctors++## [0.1.1.0] - 2020-02-29+### Added+- bindings to generichash (Blake2), thanks [@donatello](https://github.com/donatello)++### Changed+- Don't use `fail` in tests to fix compilation with GHC 8.8+- Windows install instructions added, thanks [@tmcl](https://github.com/tmcl)
+ README.md view
@@ -0,0 +1,69 @@+# Saltine 0.2.2.0 [![Hackage version](https://img.shields.io/hackage/v/saltine.svg?colorB=4FB900)](https://hackage.haskell.org/package/saltine)++A Haskell binding for @jedisct1's portable binding for djb's+NaCl. **This is an early release.** Please try it out, but don't just+yet stake your life or job on it.++It is imperative you call `sodiumInit` before using any other function.++``` haskell+import           Crypto.Saltine+import           Crypto.Saltine.Core.SecretBox+import qualified Data.ByteString.Char8 as BSC8++main = do+  sodiumInit+  k <- newKey+  n <- newNonce+  let ciphertext = secretbox k n (BSC8.pack "foobar")+  print $ secretboxOpen k n ciphertext++-- Just "foobar"+```++In+[*The Security Impact of a New Cryptographic Library*](http://cryptojedi.org/papers/coolnacl-20111201.pdf)+Bernstein, Lange, and Schwabe argue that high-level cryptographic+libraries eliminate whole spaces of cryptographic disasters which are+nigh inevitable whenever programmers use low-level crypto primitives.++* [Security Stack Exchange: Why Shouldn't We Roll Our Own?](http://security.stackexchange.com/questions/18197/why-shouldnt-we-roll-our-own)+* [Hacker News on "All the Crypto Code You've Ever Written is Probably Broken"](https://news.ycombinator.com/item?id=4779015)+* [Stack Overflow: When can you trust yourself to implement cryptography based solutions?](http://stackoverflow.com/questions/1914257/when-can-you-trust-yourself-to-implement-cryptography-based-solutions)+* [Coding Horror: Why isn't my encryption... encrypting?](http://www.codinghorror.com/blog/2009/05/why-isnt-my-encryption-encrypting.html)++Crypto is complicated, so pre-rolled solutions are important+prevention mechanisms.++[NaCl](http://nacl.cr.yp.to/) is Bernstein, Lange, and Schwabe's+solution: a high-level, performant cryptography library with a no-fuss+interface. [Saltine](http://github.com/tel/saltine) is a Haskell+binding to NaCl (via+[`libsodium`](https://github.com/jedisct1/libsodium)) which hopes to+provide even more simplicity and safety to the usage of cryptography.++Note that it's still possible to shoot yourself in the foot pretty+easily using Saltine. Nonces must always be unique which must be managed+by the library user.+[`Crypto.Saltine.Core.Stream`](https://github.com/tel/saltine/blob/master/src/Crypto/Saltine/Core/Stream.hs)+produces messages which can beundetectably tampered with in-flight.+Keys are insecurely read from disk—they may be copied and then paged+back to disk.++When uncertain, use [`Crypto.Saltine.Core.SecretBox`](https://github.com/tel/saltine/blob/master/src/Crypto/Saltine/Core/SecretBox.hs)+and [`Crypto.Saltine.Core.Box`](https://github.com/tel/saltine/blob/master/src/Crypto/Saltine/Core/Box.hs).+If you can think of ways to use Haskell's type system to enforce+security invariants, please suggest them.++To use it on Windows systems, download+[a prebuild libsodium-\*-stable-mingw.tar.gz file](https://download.libsodium.org/libsodium/releases/)+and copy the files in `libsodium-win64`  into the equivalent places+in `C:\Program Files\Haskell Platform\*\mingw`. Then just add saltine+to your cabal file and watch it go.++Tested with [`libsodium-1.0.18`](https://download.libsodium.org/libsodium/releases/).++Inspired by @thoughtpolice's+[`salt`](http://github.com/thoughtpolice/salt) library. `salt` also+binds to NaCl, but uses a Haskell managed version of djb's code+instead of `libsodium`.
+ bench/AES256GCMBench.hs view
@@ -0,0 +1,68 @@+module AES256GCMBench (benchAes256GCM, aes256GCMEnv) where++import Criterion.Main++import Control.Monad+import Control.DeepSeq+import Control.Exception+import Data.ByteString as BS++import Crypto.Saltine.Core.AEAD.AES256GCM as G++import BenchUtils++aes256GCMEnv :: IO Key+aes256GCMEnv = newKey++benchAes256GCM :: Key -> Benchmark+benchAes256GCM k = do+  let encrypt :: ByteString -> ByteString -> IO ByteString+      encrypt msg aad = newNonce >>= \n -> pure $ G.aead k n msg aad++      decrypt :: ByteString -> ByteString -> IO (Maybe ByteString)+      decrypt msg aad = do+        n <- newNonce+        let ciphertext = G.aead k n msg aad+        return $ G.aeadOpen k n ciphertext aad++      encryptDetached msg aad = newNonce >>= \n -> pure $ G.aeadDetached k n msg aad+      decryptDetached msg aad = do+        n <- newNonce+        let (t,c) = G.aeadDetached k n msg aad+        pure $ G.aeadOpenDetached k n t c aad++  bgroup "AES256GCM"+    [ bench "newKey" $ nfIO newKey+    , bgroup "aead"+      [ bench "128 B + 128 B" $ nfIO $ encrypt bs128 bs128+      , bench "128 B + 5 MB"  $ nfIO $ encrypt bs128 mb5+      , bench "1 MB + 128 B"  $ nfIO $ encrypt mb1   bs128+      , bench "1 MB + 5 B"    $ nfIO $ encrypt mb1   mb5+      , bench "5 MB + 128 B"  $ nfIO $ encrypt mb5   bs128+      , bench "5 MB + 5 MB"   $ nfIO $ encrypt mb5   mb5+      ]+    , bgroup "aead + open"+      [ bench "128 B + 128 B" $ nfIO $ decrypt bs128 bs128+      , bench "128 B + 5 MB"  $ nfIO $ decrypt bs128 mb5+      , bench "1 MB + 128 B"  $ nfIO $ decrypt mb1   bs128+      , bench "1 MB + 5 B"    $ nfIO $ decrypt mb1   mb5+      , bench "5 MB + 128 B"  $ nfIO $ decrypt mb5   bs128+      , bench "5 MB + 5 MB"   $ nfIO $ decrypt mb5   mb5+      ]+    , bgroup "aeadDetached"+      [ bench "128 B + 128 B" $ nfIO $ encryptDetached bs128 bs128+      , bench "128 B + 5 MB"  $ nfIO $ encryptDetached bs128 mb5+      , bench "1 MB + 128 B"  $ nfIO $ encryptDetached mb1   bs128+      , bench "1 MB + 5 B"    $ nfIO $ encryptDetached mb1   mb5+      , bench "5 MB + 128 B"  $ nfIO $ encryptDetached mb5   bs128+      , bench "5 MB + 5 MB"   $ nfIO $ encryptDetached mb5   mb5+      ]+    , bgroup "aeadDetached + openDetached"+      [ bench "128 B + 128 B" $ nfIO $ decryptDetached bs128 bs128+      , bench "128 B + 5 MB"  $ nfIO $ decryptDetached bs128 mb5+      , bench "1 MB + 128 B"  $ nfIO $ decryptDetached mb1   bs128+      , bench "1 MB + 5 B"    $ nfIO $ decryptDetached mb1   mb5+      , bench "5 MB + 128 B"  $ nfIO $ decryptDetached mb5   bs128+      , bench "5 MB + 5 MB"   $ nfIO $ decryptDetached mb5   mb5+      ]+    ]
+ bench/AuthBench.hs view
@@ -0,0 +1,37 @@+module AuthBench (benchAuth, authEnv) where++import Criterion.Main++import Control.Monad+import Control.DeepSeq+import Control.Exception+import Data.ByteString as BS++import Crypto.Saltine.Core.Auth++import BenchUtils+++authEnv :: IO Key+authEnv = newKey++benchAuth :: Key -> Benchmark+benchAuth k = do+  let authVerify :: ByteString -> Bool+      authVerify message = do+        let authenticator = auth k message+        verify k authenticator message++  bgroup "Auth"+    [ bench "newKey" $ nfIO newKey+    , bgroup "auth"+      [ bench "128 B" $ nf (auth k) bs128+      , bench "1 MB"  $ nf (auth k) mb1+      , bench "5 MB"  $ nf (auth k) mb5+      ]+    , bgroup "auth+verify"+      [ bench "128 B" $ nf authVerify bs128+      , bench "1 MB"  $ nf authVerify mb1+      , bench "5 MB"  $ nf authVerify mb5+      ]+    ]
+ bench/BenchUtils.hs view
@@ -0,0 +1,15 @@+module BenchUtils where++import Data.ByteString (ByteString)+import qualified Data.ByteString as BS++import Data.Text as T++bs128, kb2, mb1, mb5 :: ByteString+bs128 = BS.replicate 128     0+kb2   = BS.replicate 2000    0+mb1   = BS.replicate 1000000 0+mb5   = BS.replicate 5000000 0++s128  = T.replicate 128  (T.pack "0")+s2000 = T.replicate 2000 (T.pack "0")
+ bench/BoxBench.hs view
@@ -0,0 +1,45 @@+module BoxBench (benchBox, boxEnv) where++import Criterion.Main++import Control.Monad+import Control.DeepSeq+import Control.Exception+import Data.ByteString as BS++import Crypto.Saltine.Core.Box++import BenchUtils+++boxEnv :: IO (Keypair, Keypair)+boxEnv = do+  alice <- newKeypair+  bob   <- newKeypair+  return (alice, bob)+++benchBox :: (Keypair, Keypair) -> Benchmark+benchBox (alice, bob) = do+  let encrypt :: ByteString -> IO ByteString+      encrypt b = newNonce >>= \n -> pure $ box (publicKey bob) (secretKey alice) n b++      decrypt :: ByteString -> IO (Maybe ByteString)+      decrypt message = do+        n <- newNonce+        let ciphertext = box (publicKey alice) (secretKey bob) n message+        return $ boxOpen (publicKey bob) (secretKey alice) n ciphertext++  bgroup "Box"+    [ bench "newKeypair" $ nfIO newKeypair+    , bgroup "encrypt"+      [ bench "128 B" $ nfIO $ encrypt bs128+      , bench "1 MB"  $ nfIO $ encrypt mb1+      , bench "5 MB"  $ nfIO $ encrypt mb5+      ]+    , bgroup "encrypt+decrypt"+      [ bench "128 B" $ nfIO $ decrypt bs128+      , bench "1 MB"  $ nfIO $ decrypt mb1+      , bench "5 MB"  $ nfIO $ decrypt mb5+      ]+    ]
+ bench/ChaCha20Poly1305Bench.hs view
@@ -0,0 +1,68 @@+module ChaCha20Poly1305Bench (benchChaCha20Poly1305, chaCha20Poly1305Env) where++import Criterion.Main++import Control.Monad+import Control.DeepSeq+import Control.Exception+import Data.ByteString as BS++import Crypto.Saltine.Core.AEAD.ChaCha20Poly1305 as C++import BenchUtils++chaCha20Poly1305Env :: IO Key+chaCha20Poly1305Env = newKey++benchChaCha20Poly1305 :: Key -> Benchmark+benchChaCha20Poly1305 k = do+  let encrypt :: ByteString -> ByteString -> IO ByteString+      encrypt msg aad = newNonce >>= \n -> pure $ C.aead k n msg aad++      decrypt :: ByteString -> ByteString -> IO (Maybe ByteString)+      decrypt msg aad = do+        n <- newNonce+        let ciphertext = C.aead k n msg aad+        return $ C.aeadOpen k n ciphertext aad++      encryptDetached msg aad = newNonce >>= \n -> pure $ C.aeadDetached k n msg aad+      decryptDetached msg aad = do+        n <- newNonce+        let (t,c) = C.aeadDetached k n msg aad+        pure $ C.aeadOpenDetached k n t c aad++  bgroup "ChaCha20Poly1305"+    [ bench "newKey" $ nfIO newKey+    , bgroup "aead"+      [ bench "128 B + 128 B" $ nfIO $ encrypt bs128 bs128+      , bench "128 B + 5 MB"  $ nfIO $ encrypt bs128 mb5+      , bench "1 MB + 128 B"  $ nfIO $ encrypt mb1   bs128+      , bench "1 MB + 5 B"    $ nfIO $ encrypt mb1   mb5+      , bench "5 MB + 128 B"  $ nfIO $ encrypt mb5   bs128+      , bench "5 MB + 5 MB"   $ nfIO $ encrypt mb5   mb5+      ]+    , bgroup "aead + open"+      [ bench "128 B + 128 B" $ nfIO $ decrypt bs128 bs128+      , bench "128 B + 5 MB"  $ nfIO $ decrypt bs128 mb5+      , bench "1 MB + 128 B"  $ nfIO $ decrypt mb1   bs128+      , bench "1 MB + 5 B"    $ nfIO $ decrypt mb1   mb5+      , bench "5 MB + 128 B"  $ nfIO $ decrypt mb5   bs128+      , bench "5 MB + 5 MB"   $ nfIO $ decrypt mb5   mb5+      ]+    , bgroup "aeadDetached"+      [ bench "128 B + 128 B" $ nfIO $ encryptDetached bs128 bs128+      , bench "128 B + 5 MB"  $ nfIO $ encryptDetached bs128 mb5+      , bench "1 MB + 128 B"  $ nfIO $ encryptDetached mb1   bs128+      , bench "1 MB + 5 B"    $ nfIO $ encryptDetached mb1   mb5+      , bench "5 MB + 128 B"  $ nfIO $ encryptDetached mb5   bs128+      , bench "5 MB + 5 MB"   $ nfIO $ encryptDetached mb5   mb5+      ]+    , bgroup "aeadDetached + openDetached"+      [ bench "128 B + 128 B" $ nfIO $ decryptDetached bs128 bs128+      , bench "128 B + 5 MB"  $ nfIO $ decryptDetached bs128 mb5+      , bench "1 MB + 128 B"  $ nfIO $ decryptDetached mb1   bs128+      , bench "1 MB + 5 B"    $ nfIO $ decryptDetached mb1   mb5+      , bench "5 MB + 128 B"  $ nfIO $ decryptDetached mb5   bs128+      , bench "5 MB + 5 MB"   $ nfIO $ decryptDetached mb5   mb5+      ]+    ]
+ bench/ChaCha20Poly1305IETFBench.hs view
@@ -0,0 +1,68 @@+module ChaCha20Poly1305IETFBench (benchChaCha20Poly1305IETF, chaCha20Poly1305IETFEnv) where++import Criterion.Main++import Control.Monad+import Control.DeepSeq+import Control.Exception+import Data.ByteString as BS++import Crypto.Saltine.Core.AEAD.ChaCha20Poly1305IETF as C++import BenchUtils++chaCha20Poly1305IETFEnv :: IO Key+chaCha20Poly1305IETFEnv = newKey++benchChaCha20Poly1305IETF :: Key -> Benchmark+benchChaCha20Poly1305IETF k = do+  let encrypt :: ByteString -> ByteString -> IO ByteString+      encrypt msg aad = newNonce >>= \n -> pure $ C.aead k n msg aad++      decrypt :: ByteString -> ByteString -> IO (Maybe ByteString)+      decrypt msg aad = do+        n <- newNonce+        let ciphertext = C.aead k n msg aad+        return $ C.aeadOpen k n ciphertext aad++      encryptDetached msg aad = newNonce >>= \n -> pure $ C.aeadDetached k n msg aad+      decryptDetached msg aad = do+        n <- newNonce+        let (t,c) = C.aeadDetached k n msg aad+        pure $ C.aeadOpenDetached k n t c aad++  bgroup "ChaCha20Poly1305IETF"+    [ bench "newKey" $ nfIO newKey+    , bgroup "aead"+      [ bench "128 B + 128 B" $ nfIO $ encrypt bs128 bs128+      , bench "128 B + 5 MB"  $ nfIO $ encrypt bs128 mb5+      , bench "1 MB + 128 B"  $ nfIO $ encrypt mb1   bs128+      , bench "1 MB + 5 B"    $ nfIO $ encrypt mb1   mb5+      , bench "5 MB + 128 B"  $ nfIO $ encrypt mb5   bs128+      , bench "5 MB + 5 MB"   $ nfIO $ encrypt mb5   mb5+      ]+    , bgroup "aead + open"+      [ bench "128 B + 128 B" $ nfIO $ decrypt bs128 bs128+      , bench "128 B + 5 MB"  $ nfIO $ decrypt bs128 mb5+      , bench "1 MB + 128 B"  $ nfIO $ decrypt mb1   bs128+      , bench "1 MB + 5 B"    $ nfIO $ decrypt mb1   mb5+      , bench "5 MB + 128 B"  $ nfIO $ decrypt mb5   bs128+      , bench "5 MB + 5 MB"   $ nfIO $ decrypt mb5   mb5+      ]+    , bgroup "aeadDetached"+      [ bench "128 B + 128 B" $ nfIO $ encryptDetached bs128 bs128+      , bench "128 B + 5 MB"  $ nfIO $ encryptDetached bs128 mb5+      , bench "1 MB + 128 B"  $ nfIO $ encryptDetached mb1   bs128+      , bench "1 MB + 5 B"    $ nfIO $ encryptDetached mb1   mb5+      , bench "5 MB + 128 B"  $ nfIO $ encryptDetached mb5   bs128+      , bench "5 MB + 5 MB"   $ nfIO $ encryptDetached mb5   mb5+      ]+    , bgroup "aeadDetached + openDetached"+      [ bench "128 B + 128 B" $ nfIO $ decryptDetached bs128 bs128+      , bench "128 B + 5 MB"  $ nfIO $ decryptDetached bs128 mb5+      , bench "1 MB + 128 B"  $ nfIO $ decryptDetached mb1   bs128+      , bench "1 MB + 5 B"    $ nfIO $ decryptDetached mb1   mb5+      , bench "5 MB + 128 B"  $ nfIO $ decryptDetached mb5   bs128+      , bench "5 MB + 5 MB"   $ nfIO $ decryptDetached mb5   mb5+      ]+    ]
+ bench/ConstantTimeBench.hs view
@@ -0,0 +1,29 @@+module ConstantTimeBench (benchComparison) where++import Criterion.Main++import Control.Monad+import Control.DeepSeq+import Control.Exception+import Data.ByteString              as BS++import Crypto.Saltine.Core.Auth     as A+import Crypto.Saltine.Class+import Crypto.Saltine.Internal.Util as U++import BenchUtils++benchComparison :: Benchmark+benchComparison =+  bgroup "ConstantTime"+    [ bench "Compare two \"keys\" using ByteString comparison" $ nfIO $ do+        k1 <- randomByteString (2^20)+        k2 <- randomByteString (2^20)++        pure $ k1 == k2+    , bench "Compare two keys using constant-time comparison" $ nfIO $ do+        k1 <- randomByteString (2^20)+        k2 <- randomByteString (2^20)++        pure $ U.compare k1 k2+    ]
+ bench/HashBench.hs view
@@ -0,0 +1,40 @@+module HashBench (benchHash, hashEnv) where++import Criterion++import Control.Monad++import Crypto.Saltine.Core.Hash+import Crypto.Saltine.Core.Utils++import BenchUtils+import Data.Maybe (fromJust)++hashEnv :: IO (ShorthashKey, GenerichashKey, GenerichashOutLen)+hashEnv = do+    shk <- newShorthashKey+    ghk <- fromJust <$> newGenerichashKey 48+    let ghol = fromJust (generichashOutLen 48)++    pure (shk,ghk,ghol)++benchHash :: (ShorthashKey, GenerichashKey, GenerichashOutLen) -> Benchmark+benchHash (shk,ghk,ghol) =+  bgroup "Hash"+    [ bgroup "hash"+      [ bench "128 B" $ nf hash bs128+      , bench "1 MB"  $ nf hash mb1+      , bench "5 MB"  $ nf hash mb5+      ]+    , bgroup "shortHash"+      [ bench "128 B" $ nf (shorthash shk) bs128+      , bench "2 KB"  $ nf (shorthash shk) kb2+      , bench "1 MB"  $ nf (shorthash shk) mb1+      ]+    , bgroup "genericHash"+      [ bench "128 B" $ nf (generichash ghk bs128) ghol+      , bench "2 KB"  $ nf (generichash ghk kb2  ) ghol+      , bench "1 MB"  $ nf (generichash ghk mb1  ) ghol+      , bench "5 MB"  $ nf (generichash ghk mb5  ) ghol+      ]+    ]
+ bench/Main.hs view
@@ -0,0 +1,79 @@+import Criterion.Main++import Control.Monad+import Control.DeepSeq+import Control.Exception++import AuthBench+import OneTimeAuthBench+import BoxBench+import SecretBoxBench+import ConstantTimeBench+import HashBench+import RandomBench+import ScalarMultBench+import SignBench+import StreamBench+import PasswordBench+import AES256GCMBench+import ChaCha20Poly1305Bench+import ChaCha20Poly1305IETFBench+import XChaCha20Poly1305Bench++main :: IO ()+main = do+  authKeyToEval <- authEnv+  authKey <- evaluate $ force authKeyToEval++  oneTimeAuthKeyToEval <- oneTimeAuthEnv+  oneTimeAuthKey <- evaluate $ force oneTimeAuthKeyToEval++  boxToEval <- boxEnv+  boxKeys <- evaluate $ force boxToEval++  secretboxKeyToEval <- secretboxEnv+  secretboxKey <- evaluate $ force secretboxKeyToEval++  scmlToEval <- scalarMultEnv+  scml <- evaluate $ force scmlToEval++  signToEval <- signEnv+  signKey <- evaluate $ force signToEval++  streamKeyToEval <- streamEnv+  streamKey <- evaluate $ force streamKeyToEval++  passwordSaltToEval <- passwordEnv+  passwordSalt <- evaluate $ force passwordSaltToEval++  hashKeysToEval <- hashEnv+  hashKeys <- evaluate $ force hashKeysToEval++  aes256GCMKeyToEval <- aes256GCMEnv+  aes256GCMKey <- evaluate $ force aes256GCMKeyToEval++  chaCha20Poly1305KeyToEval <- chaCha20Poly1305Env+  chaCha20Poly1305Key <- evaluate $ force chaCha20Poly1305KeyToEval++  chaCha20Poly1305IETFKeyToEval <- chaCha20Poly1305IETFEnv+  chaCha20Poly1305IETFKey <- evaluate $ force chaCha20Poly1305IETFKeyToEval++  xChaCha20Poly1305KeyToEval <- xChaCha20Poly1305Env+  xChaCha20Poly1305Key <- evaluate $ force xChaCha20Poly1305KeyToEval++  defaultMain [+      benchAuth authKey+    , benchOneTimeAuth oneTimeAuthKey+    , benchBox boxKeys+    , benchSecretbox secretboxKey+    , benchHash hashKeys+    , benchScalarMult scml+    , benchSign signKey+    , benchStream streamKey+    , benchPassword passwordSalt+    , benchComparison+    , benchAes256GCM aes256GCMKey+    , benchChaCha20Poly1305 chaCha20Poly1305Key+    , benchChaCha20Poly1305IETF chaCha20Poly1305IETFKey+    , benchXChaCha20Poly1305 xChaCha20Poly1305Key+    ]
+ bench/OneTimeAuthBench.hs view
@@ -0,0 +1,37 @@+module OneTimeAuthBench (benchOneTimeAuth, oneTimeAuthEnv) where++import Criterion.Main++import Control.Monad+import Control.DeepSeq+import Control.Exception+import Data.ByteString as BS++import Crypto.Saltine.Core.OneTimeAuth++import BenchUtils+++oneTimeAuthEnv :: IO Key+oneTimeAuthEnv = newKey++benchOneTimeAuth :: Key -> Benchmark+benchOneTimeAuth k = do+  let authVerify :: ByteString -> Bool+      authVerify message = do+        let authenticator = auth k message+        verify k authenticator message++  bgroup "OneTimeAuth"+    [ bench "newKey" $ nfIO newKey+    , bgroup "auth"+      [ bench "128 B" $ nf (auth k) bs128+      , bench "1 MB"  $ nf (auth k) mb1+      , bench "5 MB"  $ nf (auth k) mb5+      ]+    , bgroup "auth+verify"+      [ bench "128 B" $ nf authVerify bs128+      , bench "1 MB"  $ nf authVerify mb1+      , bench "5 MB"  $ nf authVerify mb5+      ]+    ]
+ bench/PasswordBench.hs view
@@ -0,0 +1,87 @@+module PasswordBench (benchPassword, passwordEnv) where++import Criterion.Main++import Control.Monad+import Control.DeepSeq+import Control.Exception+import Data.ByteString as BS+import Data.Maybe      (fromJust)+import Data.Text       (Text)++import Crypto.Saltine.Core.Password as P++import BenchUtils++passwordEnv :: IO Salt+passwordEnv = newSalt++benchPassword :: Salt -> Benchmark+benchPassword s = do+  let hashAndVerify :: Text -> Policy ->  IO Bool+      hashAndVerify p pol = do+          h <- pwhashStr p pol+          pure $ pwhashStrVerify (fromJust h) p++      hashAndRehash :: Text -> Policy -> IO (Maybe Bool)+      hashAndRehash p pol = do+          h <- pwhashStr p pol+          pure $ needsRehash (opsPolicy pol) (memPolicy pol) (fromJust h)++  bgroup "Password"+    [ bench "newSalt" $ nfIO newSalt+    , bgroup "hash + verify"+      [ bgroup "interactive"+        [ bench "128 B" $ nfIO $ hashAndVerify s128  interactivePolicy+        , bench "2 KB"  $ nfIO $ hashAndVerify s2000 interactivePolicy+        ]+      , bgroup "moderate"+        [ bench "128 B" $ nfIO $ hashAndVerify s128  moderatePolicy+        , bench "2 KB"  $ nfIO $ hashAndVerify s2000 moderatePolicy+        ]+      , bgroup "sensitive"+        [ bench "128 B" $ nfIO $ hashAndVerify s128  sensitivePolicy+        , bench "2 KB"  $ nfIO $ hashAndVerify s2000 sensitivePolicy+        ]+      ]+    , bgroup "needsRehash"+      [ bgroup "interactive"+        [ bench "128 B" $ nfIO $ hashAndRehash s128  interactivePolicy+        , bench "2 KB"  $ nfIO $ hashAndRehash s2000 interactivePolicy+        ]+      , bgroup "moderate"+        [ bench "128 B" $ nfIO $ hashAndRehash s128  moderatePolicy+        , bench "2 KB"  $ nfIO $ hashAndRehash s2000 moderatePolicy+        ]+      , bgroup "sensitive"+        [ bench "128 B" $ nfIO $ hashAndRehash s128  sensitivePolicy+        , bench "2 KB"  $ nfIO $ hashAndRehash s2000 sensitivePolicy+        ]+      ]+    , bgroup "pwhash"+      [ bgroup "interactive"+        [ bench "128 B + 256"  $ nf (pwhash s128 (2^8 ) s) interactivePolicy+        , bench "128 B + 512"  $ nf (pwhash s128 (2^9 ) s) interactivePolicy+        , bench "128 B + 1024" $ nf (pwhash s128 (2^10) s) interactivePolicy+        , bench "128 B + 2048" $ nf (pwhash s128 (2^11) s) interactivePolicy+        , bench "128 B + 4096" $ nf (pwhash s128 (2^12) s) interactivePolicy+        , bench "128 B + 8192" $ nf (pwhash s128 (2^13) s) interactivePolicy+        ]+      , bgroup "moderate"+        [ bench "128 B + 256"  $ nf (pwhash s128 (2^8 ) s) moderatePolicy+        , bench "128 B + 512"  $ nf (pwhash s128 (2^9 ) s) moderatePolicy+        , bench "128 B + 1024" $ nf (pwhash s128 (2^10) s) moderatePolicy+        , bench "128 B + 2048" $ nf (pwhash s128 (2^11) s) moderatePolicy+        , bench "128 B + 4096" $ nf (pwhash s128 (2^12) s) moderatePolicy+        , bench "128 B + 8192" $ nf (pwhash s128 (2^13) s) moderatePolicy+        ]+      , bgroup "sensitive"+        [ bench "128 B + 256"  $ nf (pwhash s128 (2^8 ) s) sensitivePolicy+        , bench "128 B + 512"  $ nf (pwhash s128 (2^9 ) s) sensitivePolicy+        , bench "128 B + 1024" $ nf (pwhash s128 (2^10) s) sensitivePolicy+        , bench "128 B + 2048" $ nf (pwhash s128 (2^11) s) sensitivePolicy+        , bench "128 B + 4096" $ nf (pwhash s128 (2^12) s) sensitivePolicy+        , bench "128 B + 8192" $ nf (pwhash s128 (2^13) s) sensitivePolicy+        ]+      ]+    ]
+ bench/RandomBench.hs view
@@ -0,0 +1,21 @@+module RandomBench (benchRandom) where++import Criterion++import Data.ByteString (ByteString)+import Crypto.Saltine.Core.Utils++benchRandom = bgroup "random"+  [ bench "32 B"   $ nfIO (randomByteString 32          :: IO ByteString)+  , bench "128 B"  $ nfIO (randomByteString 128         :: IO ByteString)+  , bench "512 B"  $ nfIO (randomByteString 512         :: IO ByteString)+  , bench "2 KB"   $ nfIO (randomByteString 2000        :: IO ByteString)+  , bench "8 KB"   $ nfIO (randomByteString 8000        :: IO ByteString)+  , bench "32 KB"  $ nfIO (randomByteString 32000       :: IO ByteString)+  , bench "128 KB" $ nfIO (randomByteString 128000      :: IO ByteString)+  , bench "512 KB" $ nfIO (randomByteString 512000      :: IO ByteString)+  , bench "2 MB"   $ nfIO (randomByteString 2000000     :: IO ByteString)+  , bench "8 MB"   $ nfIO (randomByteString 8000000     :: IO ByteString)+  , bench "32 MB"  $ nfIO (randomByteString 32000000    :: IO ByteString)+  , bench "128 MB" $ nfIO (randomByteString 128000000   :: IO ByteString)+  ]
+ bench/ScalarMultBench.hs view
@@ -0,0 +1,35 @@+module ScalarMultBench (benchScalarMult, scalarMultEnv) where++import Criterion.Main++import Control.Monad+import Control.DeepSeq+import Control.Exception+import Data.ByteString as BS++import Data.Maybe (fromJust)++import Crypto.Saltine.Class+import Crypto.Saltine.Core.ScalarMult as S+import Crypto.Saltine.Internal.ScalarMult as Bytes+import Crypto.Saltine.Internal.Util++import BenchUtils++scalarMultEnv :: IO (GroupElement, Scalar)+scalarMultEnv = do+    bsge <- randomByteString Bytes.scalarmult_bytes+    bssc <- randomByteString Bytes.scalarmult_scalarbytes++    let ge = fromJust $ decode bsge+    let sc = fromJust $ decode bssc++    pure (ge,sc)+++benchScalarMult :: (GroupElement, Scalar) -> Benchmark+benchScalarMult (ge,sc) =+  bgroup "ScalarMult"+    [ bench "mult"     $ nf (S.mult    sc) ge+    , bench "multBase" $ nf S.multBase sc+    ]
+ bench/SecretBoxBench.hs view
@@ -0,0 +1,57 @@+module SecretBoxBench (benchSecretbox, secretboxEnv) where++import Criterion.Main++import Control.Monad+import Control.DeepSeq+import Control.Exception+import Data.ByteString as BS++import Crypto.Saltine.Core.SecretBox++import BenchUtils+++secretboxEnv :: IO Key+secretboxEnv = newKey++benchSecretbox :: Key -> Benchmark+benchSecretbox k = do+  let encrypt :: ByteString -> IO ByteString+      encrypt msg = newNonce >>= \n -> pure $ secretbox k n msg++      decrypt :: ByteString -> IO (Maybe ByteString)+      decrypt msg = do+        n <- newNonce+        let ciphertext = secretbox k n msg+        return $ secretboxOpen k n ciphertext++      encryptDetached msg = newNonce >>= \n -> pure $ secretboxDetached k n msg+      decryptDetached msg = do+          n <- newNonce+          let (t,c) = secretboxDetached k n msg+          pure $ secretboxOpenDetached k n t c++  bgroup "Box"+    [ bench "newKey" $ nfIO newKey+    , bgroup "encrypt"+      [ bench "128 B" $ nfIO $ encrypt bs128+      , bench "1 MB"  $ nfIO $ encrypt mb1+      , bench "5 MB"  $ nfIO $ encrypt mb5+      ]+    , bgroup "encrypt+decrypt"+      [ bench "128 B" $ nfIO $ decrypt bs128+      , bench "1 MB"  $ nfIO $ decrypt mb1+      , bench "5 MB"  $ nfIO $ decrypt mb5+      ]+    , bgroup "encryptDetached"+      [ bench "128 B" $ nfIO $ encryptDetached bs128+      , bench "1 MB"  $ nfIO $ encryptDetached mb1+      , bench "5 MB"  $ nfIO $ encryptDetached mb5+      ]+    , bgroup "encryptDetached+decryptDetached"+      [ bench "128 B" $ nfIO $ decryptDetached bs128+      , bench "1 MB"  $ nfIO $ decryptDetached mb1+      , bench "5 MB"  $ nfIO $ decryptDetached mb5+      ]+    ]
+ bench/SignBench.hs view
@@ -0,0 +1,53 @@+module SignBench (benchSign, signEnv) where++import Criterion.Main++import Control.Monad+import Control.DeepSeq+import Control.Exception+import Data.ByteString as BS++import Crypto.Saltine.Core.Sign as S++import BenchUtils++signEnv :: IO Keypair+signEnv = newKeypair++benchSign :: Keypair -> Benchmark+benchSign alice = do+  let sign :: ByteString -> ByteString+      sign = S.sign (secretKey alice)++      verify :: ByteString -> Bool+      verify message =+        let signed = sign message+        in case S.signOpen (publicKey alice) signed of+          Nothing -> False+          Just ms -> True++      signDetached               = S.signDetached       (secretKey alice)+      signVerifyDetached message = S.signVerifyDetached (publicKey alice) (signDetached message)+  bgroup "Sign"+    [ bench "newKeypair" $ nfIO newKeypair+    , bgroup "sign"+      [ bench "128 B" $ nf sign bs128+      , bench "1 MB"  $ nf sign mb1+      , bench "5 MB"  $ nf sign mb5+      ]+    , bgroup "sign+verify"+      [ bench "128 B" $ nf verify bs128+      , bench "1 MB"  $ nf verify mb1+      , bench "5 MB"  $ nf verify mb5+      ]+    , bgroup "signDetached"+      [ bench "128 B" $ nf signDetached bs128+      , bench "1 MB"  $ nf signDetached mb1+      , bench "5 MB"  $ nf signDetached mb5+      ]+    , bgroup "signDetached+verifyDetached"+      [ bench "128 B" $ nf signVerifyDetached bs128+      , bench "1 MB"  $ nf signVerifyDetached mb1+      , bench "5 MB"  $ nf signVerifyDetached mb5+      ]+    ]
+ bench/StreamBench.hs view
@@ -0,0 +1,37 @@+module StreamBench (benchStream, streamEnv) where++import Criterion.Main++import Control.Monad+import Control.DeepSeq+import Control.Exception+import Data.ByteString as BS++import Crypto.Saltine.Core.Stream as S++import BenchUtils++streamEnv :: IO Key+streamEnv = newKey++benchStream :: Key -> Benchmark+benchStream k = do+  let stream :: Int -> IO ByteString+      stream i = newNonce >>= \n -> pure $ S.stream k n i++      xor :: ByteString -> IO ByteString+      xor m = newNonce >>= \n -> pure $ S.xor k n m++  bgroup "Stream"+    [ bench "newKey" $ nfIO newKey+    , bgroup "stream"+      [ bench "128 B" $ nfIO $ stream (2^7)+      , bench "1 MB"  $ nfIO $ stream (2^20)+      , bench "16 MB" $ nfIO $ stream (2^24)+      ]+    , bgroup "xor"+      [ bench "128 B" $ nfIO $ xor bs128+      , bench "1 MB"  $ nfIO $ xor mb1+      , bench "5 MB"  $ nfIO $ xor mb5+      ]+    ]
+ bench/XChaCha20Poly1305Bench.hs view
@@ -0,0 +1,68 @@+module XChaCha20Poly1305Bench (benchXChaCha20Poly1305, xChaCha20Poly1305Env) where++import Criterion.Main++import Control.Monad+import Control.DeepSeq+import Control.Exception+import Data.ByteString as BS++import Crypto.Saltine.Core.AEAD.XChaCha20Poly1305 as C++import BenchUtils++xChaCha20Poly1305Env :: IO Key+xChaCha20Poly1305Env = newKey++benchXChaCha20Poly1305 :: Key -> Benchmark+benchXChaCha20Poly1305 k = do+  let encrypt :: ByteString -> ByteString -> IO ByteString+      encrypt msg aad = newNonce >>= \n -> pure $ C.aead k n msg aad++      decrypt :: ByteString -> ByteString -> IO (Maybe ByteString)+      decrypt msg aad = do+        n <- newNonce+        let ciphertext = C.aead k n msg aad+        return $ C.aeadOpen k n ciphertext aad++      encryptDetached msg aad = newNonce >>= \n -> pure $ C.aeadDetached k n msg aad+      decryptDetached msg aad = do+        n <- newNonce+        let (t,c) = C.aeadDetached k n msg aad+        pure $ C.aeadOpenDetached k n t c aad++  bgroup "XChaCha20Poly1305"+    [ bench "newKey" $ nfIO newKey+    , bgroup "aead"+      [ bench "128 B + 128 B" $ nfIO $ encrypt bs128 bs128+      , bench "128 B + 5 MB"  $ nfIO $ encrypt bs128 mb5+      , bench "1 MB + 128 B"  $ nfIO $ encrypt mb1   bs128+      , bench "1 MB + 5 B"    $ nfIO $ encrypt mb1   mb5+      , bench "5 MB + 128 B"  $ nfIO $ encrypt mb5   bs128+      , bench "5 MB + 5 MB"   $ nfIO $ encrypt mb5   mb5+      ]+    , bgroup "aead + open"+      [ bench "128 B + 128 B" $ nfIO $ decrypt bs128 bs128+      , bench "128 B + 5 MB"  $ nfIO $ decrypt bs128 mb5+      , bench "1 MB + 128 B"  $ nfIO $ decrypt mb1   bs128+      , bench "1 MB + 5 B"    $ nfIO $ decrypt mb1   mb5+      , bench "5 MB + 128 B"  $ nfIO $ decrypt mb5   bs128+      , bench "5 MB + 5 MB"   $ nfIO $ decrypt mb5   mb5+      ]+    , bgroup "aeadDetached"+      [ bench "128 B + 128 B" $ nfIO $ encryptDetached bs128 bs128+      , bench "128 B + 5 MB"  $ nfIO $ encryptDetached bs128 mb5+      , bench "1 MB + 128 B"  $ nfIO $ encryptDetached mb1   bs128+      , bench "1 MB + 5 B"    $ nfIO $ encryptDetached mb1   mb5+      , bench "5 MB + 128 B"  $ nfIO $ encryptDetached mb5   bs128+      , bench "5 MB + 5 MB"   $ nfIO $ encryptDetached mb5   mb5+      ]+    , bgroup "aeadDetached + openDetached"+      [ bench "128 B + 128 B" $ nfIO $ decryptDetached bs128 bs128+      , bench "128 B + 5 MB"  $ nfIO $ decryptDetached bs128 mb5+      , bench "1 MB + 128 B"  $ nfIO $ decryptDetached mb1   bs128+      , bench "1 MB + 5 B"    $ nfIO $ decryptDetached mb1   mb5+      , bench "5 MB + 128 B"  $ nfIO $ decryptDetached mb5   bs128+      , bench "5 MB + 5 MB"   $ nfIO $ decryptDetached mb5   mb5+      ]+    ]
saltine.cabal view
@@ -1,5 +1,7 @@+cabal-version: 2.0+ name:                saltine-version:             0.1.0.1+version:             0.2.2.0 synopsis:            Cryptography that's easy to digest (NaCl/libsodium bindings). description: @@ -18,6 +20,10 @@   /Saltine/ is a Haskell binding to the NaCl primitives going through   Sodium for build convenience and, eventually, portability. +extra-source-files:+                     README.md+                     CHANGELOG.md+ license:             MIT license-file:        LICENSE author:              Joseph Abrahamson@@ -26,8 +32,7 @@ copyright:           Copyright (c) Joseph Abrahamson 2013 category:            Cryptography build-type:          Simple-cabal-version:       >=1.10-tested-with:         GHC==7.8.4, GHC==7.10.3, GHC==8.0.2, GHC==8.2.1+tested-with:         GHC==8.0.2, GHC==8.2.2, GHC==8.4.4, GHC==8.6.5, GHC==8.8.4, GHC==8.10.7, GHC==9.0.2, GHC==9.2.6, GHC==9.4.4  source-repository head   type: git@@ -37,9 +42,13 @@   hs-source-dirs:     src   exposed-modules:                   Crypto.Saltine-                  Crypto.Saltine.Internal.ByteSizes+                  Crypto.Saltine.Class                   Crypto.Saltine.Core.SecretBox                   Crypto.Saltine.Core.AEAD+                  Crypto.Saltine.Core.AEAD.AES256GCM+                  Crypto.Saltine.Core.AEAD.ChaCha20Poly1305+                  Crypto.Saltine.Core.AEAD.ChaCha20Poly1305IETF+                  Crypto.Saltine.Core.AEAD.XChaCha20Poly1305                   Crypto.Saltine.Core.Box                   Crypto.Saltine.Core.Stream                   Crypto.Saltine.Core.Auth@@ -47,23 +56,40 @@                   Crypto.Saltine.Core.Sign                   Crypto.Saltine.Core.Hash                   Crypto.Saltine.Core.ScalarMult+                  Crypto.Saltine.Core.Password                   Crypto.Saltine.Core.Utils-                  Crypto.Saltine.Class-  other-modules:+                  Crypto.Saltine.Internal.AEAD.AES256GCM+                  Crypto.Saltine.Internal.AEAD.ChaCha20Poly1305+                  Crypto.Saltine.Internal.AEAD.ChaCha20Poly1305IETF+                  Crypto.Saltine.Internal.AEAD.XChaCha20Poly1305+                  Crypto.Saltine.Internal.Auth+                  Crypto.Saltine.Internal.Box+                  Crypto.Saltine.Internal.ByteSizes+                  Crypto.Saltine.Internal.Hash+                  Crypto.Saltine.Internal.OneTimeAuth+                  Crypto.Saltine.Internal.Password+                  Crypto.Saltine.Internal.ScalarMult+                  Crypto.Saltine.Internal.SecretBox+                  Crypto.Saltine.Internal.Sign+                  Crypto.Saltine.Internal.Stream                   Crypto.Saltine.Internal.Util+  other-modules:    if os(windows)     extra-libraries: sodium   else-    pkgconfig-depends: libsodium >= 1.0.13+    pkgconfig-depends: libsodium >= 1.0.18    cc-options:         -Wall   ghc-options:        -Wall -funbox-strict-fields   default-language:   Haskell2010   build-depends:-                base >= 4.5 && < 5-              , bytestring-              , profunctors+                base        >= 4.5    && < 5+              , bytestring  >= 0.10.8 && < 0.13+              , deepseq     >= 1.4    && < 1.6+              , profunctors >= 5.3    && < 5.7+              , hashable+              , text       ^>= 1.2 || >= 2.0 && < 2.2  test-suite tests   type:    exitcode-stdio-1.0@@ -71,22 +97,63 @@   other-modules:                 AuthProperties                 BoxProperties+                HashProperties                 OneTimeAuthProperties+                PasswordProperties                 ScalarMultProperties                 SecretBoxProperties                 SealedBoxProperties                 SignProperties                 StreamProperties-                AEADProperties+                AEAD.AES256GCMProperties+                AEAD.ChaCha20Poly1305IETFProperties+                AEAD.ChaCha20Poly1305Properties+                AEAD.XChaCha20Poly1305Properties                 Util+                UtilProperties   ghc-options: -Wall -threaded -rtsopts   hs-source-dirs: tests   default-language: Haskell2010   build-depends:-                base >= 4.5 && < 5+                base >= 4.7 && < 5               , saltine               , bytestring+              , text               , QuickCheck               , test-framework-quickcheck2               , test-framework               , semigroups++benchmark benchmarks+  type: exitcode-stdio-1.0+  main-is: Main.hs+  hs-source-dirs:+      bench+  ghc-options: -rtsopts -threaded -with-rtsopts=-N -O2+  extra-libraries:+      sodium+  build-depends:+      base+    , bytestring+    , text+    , criterion+    , deepseq+    , saltine+  other-modules:+      AuthBench+      OneTimeAuthBench+      ConstantTimeBench+      BoxBench+      SecretBoxBench+      HashBench+      RandomBench+      PasswordBench+      ScalarMultBench+      SignBench+      StreamBench+      BenchUtils+      AES256GCMBench+      ChaCha20Poly1305Bench+      ChaCha20Poly1305IETFBench+      XChaCha20Poly1305Bench+  default-language: Haskell2010
src/Crypto/Saltine/Class.hs view
@@ -1,3 +1,5 @@+-- {-# LANGUAGE FlexibleInstances #-}+ -- | -- Module      : Crypto.Saltine.Class -- Copyright   : (c) Joseph Abrahamson 2013@@ -13,9 +15,8 @@   IsNonce (..)   ) where -import           Control.Applicative-import           Data.Profunctor-import           Data.ByteString     (ByteString)+import Data.Profunctor+import Data.ByteString (ByteString)  -- | Class for all keys and nonces in Saltine which have a -- representation as ByteString. 'encoded' is a 'Prism' of
src/Crypto/Saltine/Core/AEAD.hs view
@@ -1,9 +1,10 @@ -- | -- Module      : Crypto.Saltine.Core.AEAD -- Copyright   : (c) Thomas DuBuisson 2017+--               (c) Max Amanshauser 2021 -- License     : MIT ----- Maintainer  : me@jspha.com+-- Maintainer  : max@lambdalifting.org -- Stability   : experimental -- Portability : non-portable --@@ -28,229 +29,19 @@ -- Note that the length is not hidden. Note also that it is the -- caller's responsibility to ensure the uniqueness of nonces—for -- example, by using nonce 1 for the first message, nonce 2 for the--- second message, etc. Nonces are long enough that randomly generated--- nonces have negligible risk of collision.+-- second message, etc. With XChaCha20Poly1305 nonces are long enough+-- that you can also generate nonces randomly as they have negligible+-- risk of collision.+--+-- The keysize is identical for all the *ChaCha20Poly1305* variants,+-- but the nonce length differs. Since libsodium keeps separate definitions,+-- we do the same.+--+-- This module reexports the XChaCha20Poly1305 variant, which is the+-- recommended one.  module Crypto.Saltine.Core.AEAD (-  Key, Nonce,-  aead, aeadOpen,-  aeadDetached, aeadOpenDetached,-  newKey, newNonce+  module X   ) where -import           Crypto.Saltine.Class-import           Crypto.Saltine.Internal.Util-import qualified Crypto.Saltine.Internal.ByteSizes as Bytes--import           Control.Applicative-import           Foreign.C-import           Foreign.Ptr-import qualified Data.ByteString                   as S-import           Data.ByteString                     (ByteString)---- $types---- | An opaque 'secretbox' cryptographic key.-newtype Key = Key ByteString deriving (Eq, Ord)--instance IsEncoding Key where-  decode v = if S.length v == Bytes.secretBoxKey-           then Just (Key v)-           else Nothing-  {-# INLINE decode #-}-  encode (Key v) = v-  {-# INLINE encode #-}---- | An opaque 'secretbox' nonce.-newtype Nonce = Nonce ByteString deriving (Eq, Ord)--instance IsEncoding Nonce where-  decode v = if S.length v == Bytes.secretBoxNonce-           then Just (Nonce v)-           else Nothing-  {-# INLINE decode #-}-  encode (Nonce v) = v-  {-# INLINE encode #-}--instance IsNonce Nonce where-  zero            = Nonce (S.replicate Bytes.secretBoxNonce 0)-  nudge (Nonce n) = Nonce (nudgeBS n)---- | Creates a random key of the correct size for 'secretbox'.-newKey :: IO Key-newKey = Key <$> randomByteString Bytes.secretBoxKey---- | Creates a random nonce of the correct size for 'secretbox'.-newNonce :: IO Nonce-newNonce = Nonce <$> randomByteString Bytes.secretBoxNonce----- | Encrypts a message. It is infeasible for an attacker to decrypt--- the message so long as the 'Nonce' is never repeated.-aead :: Key -> Nonce-          -> ByteString-          -- ^ Message-          -> ByteString-          -- ^ AAD-          -> ByteString-          -- ^ Ciphertext-aead (Key key) (Nonce nonce) msg aad =-  snd . buildUnsafeByteString clen $ \pc ->-    constByteStrings [key, msg, aad, nonce] $ \-      [(pk, _), (pm, _), (pa, _), (pn, _)] ->-          c_aead pc nullPtr pm (fromIntegral mlen) pa (fromIntegral alen) nullPtr pn pk-  where mlen    = S.length msg-        alen    = S.length aad-        clen    = mlen + Bytes.aead_xchacha20poly1305_ietf_ABYTES---- | Decrypts a message. Returns 'Nothing' if the keys and message do--- not match.-aeadOpen :: Key -> Nonce -         -> ByteString-         -- ^ Ciphertext-         -> ByteString-         -- ^ AAD-         -> Maybe ByteString-         -- ^ Message-aeadOpen (Key key) (Nonce nonce) cipher aad =-  let (err, vec) = buildUnsafeByteString mlen $ \pm ->-        constByteStrings [key, cipher, aad, nonce] $ \-          [(pk, _), (pc, _), (pa, _), (pn, _)] ->-            c_aead_open pm nullPtr nullPtr pc (fromIntegral clen) pa (fromIntegral alen) pn pk-  in hush . handleErrno err $ vec-  where clen   = S.length cipher-        alen   = S.length aad-        mlen   = clen - Bytes.aead_xchacha20poly1305_ietf_ABYTES---- | Encrypts a message. It is infeasible for an attacker to decrypt--- the message so long as the 'Nonce' is never repeated.-aeadDetached :: Key -> Nonce-          -> ByteString-          -- ^ Message-          -> ByteString-          -- ^ AAD-          -> (ByteString,ByteString)-          -- ^ Tag, Ciphertext-aeadDetached (Key key) (Nonce nonce) msg aad =-  buildUnsafeByteString clen $ \pc ->-   fmap snd . buildUnsafeByteString' tlen $ \pt ->-    constByteStrings [key, msg, aad, nonce] $ \-      [(pk, _), (pm, _), (pa, _), (pn, _)] ->-          c_aead_detached pc pt nullPtr pm (fromIntegral mlen) pa (fromIntegral alen) nullPtr pn pk-  where mlen    = S.length msg-        alen    = S.length aad-        clen    = mlen-        tlen    = Bytes.aead_xchacha20poly1305_ietf_ABYTES---- | Decrypts a message. Returns 'Nothing' if the keys and message do--- not match.-aeadOpenDetached :: Key -> Nonce-         -> ByteString-         -- ^ Tag-         -> ByteString-         -- ^ Ciphertext-         -> ByteString-         -- ^ AAD-         -> Maybe ByteString-         -- ^ Message-aeadOpenDetached (Key key) (Nonce nonce) tag cipher aad-    | S.length tag /= tlen = Nothing-    | otherwise =-  let (err, vec) = buildUnsafeByteString len $ \pm ->-        constByteStrings [key, tag, cipher, aad, nonce] $ \-          [(pk, _), (pt, _), (pc, _), (pa, _), (pn, _)] ->-            c_aead_open_detached pm nullPtr pc (fromIntegral len) pt pa (fromIntegral alen) pn pk-  in hush . handleErrno err $ vec-  where len    = S.length cipher-        alen   = S.length aad-        tlen   = Bytes.aead_xchacha20poly1305_ietf_ABYTES---- | The aead C API uses C strings. Always returns 0.-foreign import ccall "crypto_aead_xchacha20poly1305_ietf_encrypt"-  c_aead :: Ptr CChar-              -- ^ Cipher output buffer-              -> Ptr CULLong-              -- ^ Cipher output bytes used-              -> Ptr CChar-              -- ^ Constant message input buffer-              -> CULLong-              -- ^ Length of message input buffer-              -> Ptr CChar-              -- ^ Constant aad input buffer-              -> CULLong-              -- ^ Length of aad input buffer-              -> Ptr CChar-              -- ^ Unused 'nsec' value (must be NULL)-              -> Ptr CChar-              -- ^ Constant nonce buffer-              -> Ptr CChar-              -- ^ Constant key buffer-              -> IO CInt---- | The aead open C API uses C strings. Returns 0 if successful.-foreign import ccall "crypto_aead_xchacha20poly1305_ietf_decrypt"-  c_aead_open :: Ptr CChar-              -- ^ Message output buffer-              -> Ptr CULLong-              -- ^ Message output bytes used-              -> Ptr CChar-              -- ^ Unused 'nsec' value (must be NULL)-              -> Ptr CChar-              -- ^ Constant ciphertext input buffer-              -> CULLong-              -- ^ Length of ciphertext input buffer-              -> Ptr CChar-              -- ^ Constant aad input buffer-              -> CULLong-              -- ^ Length of aad input buffer-              -> Ptr CChar-              -- ^ Constant nonce buffer-              -> Ptr CChar-              -- ^ Constant key buffer-              -> IO CInt---- | The aead C API uses C strings. Always returns 0.-foreign import ccall "crypto_aead_xchacha20poly1305_ietf_encrypt_detached"-  c_aead_detached :: Ptr CChar-              -- ^ Cipher output buffer-              -> Ptr CChar-              -- ^ Tag output buffer-              -> Ptr CULLong-              -- ^ Tag bytes used-              -> Ptr CChar-              -- ^ Constant message input buffer-              -> CULLong-              -- ^ Length of message input buffer-              -> Ptr CChar-              -- ^ Constant aad input buffer-              -> CULLong-              -- ^ Length of aad input buffer-              -> Ptr CChar-              -- ^ Unused 'nsec' value (must be NULL)-              -> Ptr CChar-              -- ^ Constant nonce buffer-              -> Ptr CChar-              -- ^ Constant key buffer-              -> IO CInt---- | The aead open C API uses C strings. Returns 0 if successful.-foreign import ccall "crypto_aead_xchacha20poly1305_ietf_decrypt_detached"-  c_aead_open_detached :: Ptr CChar-              -- ^ Message output buffer-              -> Ptr CChar-              -- ^ Unused 'nsec' value (must be NULL)-              -> Ptr CChar-              -- ^ Constant ciphertext input buffer-              -> CULLong-              -- ^ Length of ciphertext input buffer-              -> Ptr CChar-              -- ^ Constant tag input buffer-              -> Ptr CChar-              -- ^ Constant aad input buffer-              -> CULLong-              -- ^ Length of aad input buffer-              -> Ptr CChar-              -- ^ Constant nonce buffer-              -> Ptr CChar-              -- ^ Constant key buffer-              -> IO CInt+import Crypto.Saltine.Core.AEAD.XChaCha20Poly1305 as X
+ src/Crypto/Saltine/Core/AEAD/AES256GCM.hs view
@@ -0,0 +1,149 @@+-- |+-- Module      : Crypto.Saltine.Core.AEAD.AES256GCM+-- Copyright   : (c) Thomas DuBuisson 2017+--               (c) Max Amanshauser 2021+-- License     : MIT+--+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable+--+-- Secret-key authenticated encryption with additional data (AEAD):+-- "Crypto.Saltine.Core.AEAD.AES256GCM"+--+-- Using this module is not recommended. Don't use unless you have to.+-- Keep in mind its limitations: https://doc.libsodium.org/secret-key_cryptography/aead+--+-- Unless you know for certain the CPU your program will run on supports+-- Intel SSSE3, AES-NI and CLMUL, you should run @aead_aes256gcm_available@+-- first and only proceed if the result is True.+--+-- Generating nonces for the functions in this module randomly+-- is not recommended, due to the risk of generating collisions.++module Crypto.Saltine.Core.AEAD.AES256GCM (+  Key, Nonce,+  aead_aes256gcm_available,+  aead, aeadOpen,+  aeadDetached, aeadOpenDetached,+  newKey, newNonce+  ) where++import Crypto.Saltine.Internal.AEAD.AES256GCM+            ( c_aead_aes256gcm_is_available+            , c_aead+            , c_aead_open+            , c_aead_detached+            , c_aead_open_detached+            , Key(..)+            , Nonce(..)+            )+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Foreign.Ptr+import System.IO.Unsafe             (unsafePerformIO)++import qualified Crypto.Saltine.Internal.AEAD.AES256GCM as Bytes+import qualified Data.ByteString                        as S++-- | Creates a random 'AES256GCM' key+newKey :: IO Key+newKey = Key <$> randomByteString Bytes.aead_aes256gcm_keybytes++-- | Creates a random 'AES256GCM' nonce+newNonce :: IO Nonce+newNonce = Nonce <$> randomByteString Bytes.aead_aes256gcm_npubbytes+++{-# NOINLINE aead_aes256gcm_available #-}+aead_aes256gcm_available :: Bool+aead_aes256gcm_available =+    unsafePerformIO c_aead_aes256gcm_is_available == 1++-- | Encrypts a message. It is infeasible for an attacker to decrypt+-- the message so long as the 'Nonce' is never repeated.+aead+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Message+    -> ByteString+    -- ^ AAD+    -> ByteString+    -- ^ Ciphertext+aead (Key key) (Nonce nonce) msg aad =+  snd . buildUnsafeByteString clen $ \pc ->+    constByteStrings [key, msg, aad, nonce] $ \+      [(pk, _), (pm, _), (pa, _), (pn, _)] ->+          c_aead pc nullPtr pm (fromIntegral mlen) pa (fromIntegral alen) nullPtr pn pk+  where mlen    = S.length msg+        alen    = S.length aad+        clen    = mlen + Bytes.aead_aes256gcm_abytes++-- | Decrypts a message. Returns 'Nothing' if the keys and message do+-- not match.+aeadOpen+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Ciphertext+    -> ByteString+    -- ^ AAD+    -> Maybe ByteString+    -- ^ Message+aeadOpen (Key key) (Nonce nonce) cipher aad = do+  let clen   = S.length cipher+      alen   = S.length aad+  mlen <- clen `safeSubtract` Bytes.aead_aes256gcm_abytes+  let (err, vec) = buildUnsafeByteString mlen $ \pm ->+        constByteStrings [key, cipher, aad, nonce] $ \+          [(pk, _), (pc, _), (pa, _), (pn, _)] ->+            c_aead_open pm nullPtr nullPtr pc (fromIntegral clen) pa (fromIntegral alen) pn pk+  hush . handleErrno err $ vec++-- | Encrypts a message. It is infeasible for an attacker to decrypt+-- the message so long as the 'Nonce' is never repeated.+aeadDetached+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Message+    -> ByteString+    -- ^ AAD+    -> (ByteString,ByteString)+    -- ^ Tag, Ciphertext+aeadDetached (Key key) (Nonce nonce) msg aad =+  buildUnsafeByteString clen $ \pc ->+   fmap snd . buildUnsafeByteString' tlen $ \pt ->+    constByteStrings [key, msg, aad, nonce] $ \+      [(pk, _), (pm, _), (pa, _), (pn, _)] ->+          c_aead_detached pc pt nullPtr pm (fromIntegral mlen) pa (fromIntegral alen) nullPtr pn pk+  where mlen    = S.length msg+        alen    = S.length aad+        clen    = mlen+        tlen    = Bytes.aead_aes256gcm_abytes++-- | Decrypts a message. Returns 'Nothing' if the keys and message do+-- not match.+aeadOpenDetached+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Tag+    -> ByteString+    -- ^ Ciphertext+    -> ByteString+    -- ^ AAD+    -> Maybe ByteString+    -- ^ Message+aeadOpenDetached (Key key) (Nonce nonce) tag cipher aad+    | S.length tag /= tlen = Nothing+    | otherwise =+  let (err, vec) = buildUnsafeByteString len $ \pm ->+        constByteStrings [key, tag, cipher, aad, nonce] $ \+          [(pk, _), (pt, _), (pc, _), (pa, _), (pn, _)] ->+            c_aead_open_detached pm nullPtr pc (fromIntegral len) pt pa (fromIntegral alen) pn pk+  in hush . handleErrno err $ vec+  where len    = S.length cipher+        alen   = S.length aad+        tlen   = Bytes.aead_aes256gcm_abytes
+ src/Crypto/Saltine/Core/AEAD/ChaCha20Poly1305.hs view
@@ -0,0 +1,134 @@+-- |+-- Module      : Crypto.Saltine.Core.AEAD.ChaCha20Poly1305+-- Copyright   : (c) Thomas DuBuisson 2017+--               (c) Max Amanshauser 2021+-- License     : MIT+--+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable+--+-- Secret-key authenticated encryption with additional data (AEAD):+-- "Crypto.Saltine.Core.AEAD.ChaCha20Poly1305"+--+-- Generating nonces for the functions in this module randomly+-- is not recommended, due to the risk of generating collisions.++module Crypto.Saltine.Core.AEAD.ChaCha20Poly1305 (+  Key, Nonce,+  aead, aeadOpen,+  aeadDetached, aeadOpenDetached,+  newKey, newNonce+  ) where++import Crypto.Saltine.Internal.AEAD.ChaCha20Poly1305+            ( c_aead+            , c_aead_open+            , c_aead_detached+            , c_aead_open_detached+            , Key(..)+            , Nonce(..)+            )+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Foreign.Ptr++import qualified Crypto.Saltine.Internal.AEAD.ChaCha20Poly1305  as Bytes+import qualified Data.ByteString                                as S++-- | Creates a random 'ChaCha20Poly1305' key+newKey :: IO Key+newKey = Key <$> randomByteString Bytes.aead_chacha20poly1305_keybytes++-- | Creates a random 'ChaCha20Poly1305' nonce+newNonce :: IO Nonce+newNonce = Nonce <$> randomByteString Bytes.aead_chacha20poly1305_npubbytes+++-- | Encrypts a message. It is infeasible for an attacker to decrypt+-- the message so long as the 'Nonce' is never repeated.+aead+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Message+    -> ByteString+    -- ^ AAD+    -> ByteString+    -- ^ Ciphertext+aead (Key key) (Nonce nonce) msg aad =+  snd . buildUnsafeByteString clen $ \pc ->+    constByteStrings [key, msg, aad, nonce] $ \+      [(pk, _), (pm, _), (pa, _), (pn, _)] ->+          c_aead pc nullPtr pm (fromIntegral mlen) pa (fromIntegral alen) nullPtr pn pk+  where mlen    = S.length msg+        alen    = S.length aad+        clen    = mlen + Bytes.aead_chacha20poly1305_abytes++-- | Decrypts a message. Returns 'Nothing' if the keys and message do+-- not match.+aeadOpen+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Ciphertext+    -> ByteString+    -- ^ AAD+    -> Maybe ByteString+    -- ^ Message+aeadOpen (Key key) (Nonce nonce) cipher aad = do+  let clen   = S.length cipher+      alen   = S.length aad+  mlen <- clen `safeSubtract` Bytes.aead_chacha20poly1305_abytes+  let (err, vec) = buildUnsafeByteString mlen $ \pm ->+        constByteStrings [key, cipher, aad, nonce] $ \+          [(pk, _), (pc, _), (pa, _), (pn, _)] ->+            c_aead_open pm nullPtr nullPtr pc (fromIntegral clen) pa (fromIntegral alen) pn pk+  hush . handleErrno err $ vec++-- | Encrypts a message. It is infeasible for an attacker to decrypt+-- the message so long as the 'Nonce' is never repeated.+aeadDetached+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Message+    -> ByteString+    -- ^ AAD+    -> (ByteString,ByteString)+    -- ^ Tag, Ciphertext+aeadDetached (Key key) (Nonce nonce) msg aad =+  buildUnsafeByteString clen $ \pc ->+   fmap snd . buildUnsafeByteString' tlen $ \pt ->+    constByteStrings [key, msg, aad, nonce] $ \+      [(pk, _), (pm, _), (pa, _), (pn, _)] ->+          c_aead_detached pc pt nullPtr pm (fromIntegral mlen) pa (fromIntegral alen) nullPtr pn pk+  where mlen    = S.length msg+        alen    = S.length aad+        clen    = mlen+        tlen    = Bytes.aead_chacha20poly1305_abytes++-- | Decrypts a message. Returns 'Nothing' if the keys and message do+-- not match.+aeadOpenDetached+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Tag+    -> ByteString+    -- ^ Ciphertext+    -> ByteString+    -- ^ AAD+    -> Maybe ByteString+    -- ^ Message+aeadOpenDetached (Key key) (Nonce nonce) tag cipher aad+    | S.length tag /= tlen = Nothing+    | otherwise =+  let (err, vec) = buildUnsafeByteString len $ \pm ->+        constByteStrings [key, tag, cipher, aad, nonce] $ \+          [(pk, _), (pt, _), (pc, _), (pa, _), (pn, _)] ->+            c_aead_open_detached pm nullPtr pc (fromIntegral len) pt pa (fromIntegral alen) pn pk+  in hush . handleErrno err $ vec+  where len    = S.length cipher+        alen   = S.length aad+        tlen   = Bytes.aead_chacha20poly1305_abytes
+ src/Crypto/Saltine/Core/AEAD/ChaCha20Poly1305IETF.hs view
@@ -0,0 +1,134 @@+-- |+-- Module      : Crypto.Saltine.Core.AEAD.ChaCha20Poly1305IETF+-- Copyright   : (c) Thomas DuBuisson 2017+--               (c) Max Amanshauser 2021+-- License     : MIT+--+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable+--+-- Secret-key authenticated encryption with additional data (AEAD):+-- "Crypto.Saltine.Core.AEAD.ChaCha20Poly1305IETF"+--+-- Generating nonces for the functions in this module randomly+-- is not recommended, due to the risk of generating collisions.++module Crypto.Saltine.Core.AEAD.ChaCha20Poly1305IETF (+  Key, Nonce,+  aead, aeadOpen,+  aeadDetached, aeadOpenDetached,+  newKey, newNonce+  ) where++import Crypto.Saltine.Internal.AEAD.ChaCha20Poly1305IETF+        ( c_aead+        , c_aead_open+        , c_aead_detached+        , c_aead_open_detached+        , Key(..)+        , Nonce(..)+        )+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Foreign.Ptr++import qualified Crypto.Saltine.Internal.AEAD.ChaCha20Poly1305IETF  as Bytes+import qualified Data.ByteString                                    as S++-- | Creates a random 'ChaCha20Poly1305IETF' key+newKey :: IO Key+newKey = Key <$> randomByteString Bytes.aead_chacha20poly1305_ietf_keybytes++-- | Creates a random 'ChaCha20Poly1305IETF' nonce+newNonce :: IO Nonce+newNonce = Nonce <$> randomByteString Bytes.aead_chacha20poly1305_ietf_npubbytes+++-- | Encrypts a message. It is infeasible for an attacker to decrypt+-- the message so long as the 'Nonce' is never repeated.+aead+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Message+    -> ByteString+    -- ^ AAD+    -> ByteString+    -- ^ Ciphertext+aead (Key key) (Nonce nonce) msg aad =+  snd . buildUnsafeByteString clen $ \pc ->+    constByteStrings [key, msg, aad, nonce] $ \+      [(pk, _), (pm, _), (pa, _), (pn, _)] ->+          c_aead pc nullPtr pm (fromIntegral mlen) pa (fromIntegral alen) nullPtr pn pk+  where mlen    = S.length msg+        alen    = S.length aad+        clen    = mlen + Bytes.aead_chacha20poly1305_ietf_abytes++-- | Decrypts a message. Returns 'Nothing' if the keys and message do+-- not match.+aeadOpen+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Ciphertext+    -> ByteString+    -- ^ AAD+    -> Maybe ByteString+    -- ^ Message+aeadOpen (Key key) (Nonce nonce) cipher aad = do+  let clen   = S.length cipher+      alen   = S.length aad+  mlen <- clen `safeSubtract` Bytes.aead_chacha20poly1305_ietf_abytes+  let (err, vec) = buildUnsafeByteString mlen $ \pm ->+        constByteStrings [key, cipher, aad, nonce] $ \+          [(pk, _), (pc, _), (pa, _), (pn, _)] ->+            c_aead_open pm nullPtr nullPtr pc (fromIntegral clen) pa (fromIntegral alen) pn pk+  hush . handleErrno err $ vec++-- | Encrypts a message. It is infeasible for an attacker to decrypt+-- the message so long as the 'Nonce' is never repeated.+aeadDetached+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Message+    -> ByteString+    -- ^ AAD+    -> (ByteString,ByteString)+    -- ^ Tag, Ciphertext+aeadDetached (Key key) (Nonce nonce) msg aad =+  buildUnsafeByteString clen $ \pc ->+   fmap snd . buildUnsafeByteString' tlen $ \pt ->+    constByteStrings [key, msg, aad, nonce] $ \+      [(pk, _), (pm, _), (pa, _), (pn, _)] ->+          c_aead_detached pc pt nullPtr pm (fromIntegral mlen) pa (fromIntegral alen) nullPtr pn pk+  where mlen    = S.length msg+        alen    = S.length aad+        clen    = mlen+        tlen    = Bytes.aead_chacha20poly1305_ietf_abytes++-- | Decrypts a message. Returns 'Nothing' if the keys and message do+-- not match.+aeadOpenDetached+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Tag+    -> ByteString+    -- ^ Ciphertext+    -> ByteString+    -- ^ AAD+    -> Maybe ByteString+    -- ^ Message+aeadOpenDetached (Key key) (Nonce nonce) tag cipher aad+    | S.length tag /= tlen = Nothing+    | otherwise =+  let (err, vec) = buildUnsafeByteString len $ \pm ->+        constByteStrings [key, tag, cipher, aad, nonce] $ \+          [(pk, _), (pt, _), (pc, _), (pa, _), (pn, _)] ->+            c_aead_open_detached pm nullPtr pc (fromIntegral len) pt pa (fromIntegral alen) pn pk+  in hush . handleErrno err $ vec+  where len    = S.length cipher+        alen   = S.length aad+        tlen   = Bytes.aead_chacha20poly1305_ietf_abytes
+ src/Crypto/Saltine/Core/AEAD/XChaCha20Poly1305.hs view
@@ -0,0 +1,134 @@+-- |+-- Module      : Crypto.Saltine.Core.AEAD.XChaCha20Poly1305+-- Copyright   : (c) Thomas DuBuisson 2017+-- License     : MIT+--+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable+--+-- Secret-key authenticated encryption with additional data (AEAD):+-- "Crypto.Saltine.Core.AEAD.XChaCha20Poly1305"+--+-- Nonces are long enough that randomly generated+-- nonces have negligible risk of collision.++module Crypto.Saltine.Core.AEAD.XChaCha20Poly1305 (+  Key, Nonce,+  aead, aeadOpen,+  aeadDetached, aeadOpenDetached,+  newKey, newNonce+  ) where++import Crypto.Saltine.Internal.AEAD.XChaCha20Poly1305+            ( c_aead+            , c_aead_open+            , c_aead_detached+            , c_aead_open_detached+            , Key(..)+            , Nonce(..)+            )+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Foreign.Ptr++import qualified Crypto.Saltine.Internal.AEAD.XChaCha20Poly1305 as Bytes+import qualified Data.ByteString                                as S+++-- | Creates a random 'XChaCha20Poly1305' key+newKey :: IO Key+newKey = Key <$> randomByteString Bytes.aead_xchacha20poly1305_ietf_keybytes++-- | Creates a random 'XChaCha20Poly1305' nonce+newNonce :: IO Nonce+newNonce = Nonce <$> randomByteString Bytes.aead_xchacha20poly1305_ietf_npubbytes+++-- | Encrypts a message. It is infeasible for an attacker to decrypt+-- the message so long as the 'Nonce' is never repeated.+aead+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Message+    -> ByteString+    -- ^ AAD+    -> ByteString+    -- ^ Ciphertext+aead (Key key) (Nonce nonce) msg aad =+  snd . buildUnsafeByteString clen $ \pc ->+    constByteStrings [key, msg, aad, nonce] $ \+      [(pk, _), (pm, _), (pa, _), (pn, _)] ->+          c_aead pc nullPtr pm (fromIntegral mlen) pa (fromIntegral alen) nullPtr pn pk+  where mlen    = S.length msg+        alen    = S.length aad+        clen    = mlen + Bytes.aead_xchacha20poly1305_ietf_abytes++-- | Decrypts a message. Returns 'Nothing' if the keys and message do+-- not match.+aeadOpen+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Ciphertext+    -> ByteString+    -- ^ AAD+    -> Maybe ByteString+    -- ^ Message+aeadOpen (Key key) (Nonce nonce) cipher aad = do+  let clen   = S.length cipher+      alen   = S.length aad+  mlen <- clen `safeSubtract` Bytes.aead_xchacha20poly1305_ietf_abytes+  let (err, vec) = buildUnsafeByteString mlen $ \pm ->+        constByteStrings [key, cipher, aad, nonce] $ \+          [(pk, _), (pc, _), (pa, _), (pn, _)] ->+            c_aead_open pm nullPtr nullPtr pc (fromIntegral clen) pa (fromIntegral alen) pn pk+  hush . handleErrno err $ vec++-- | Encrypts a message. It is infeasible for an attacker to decrypt+-- the message so long as the 'Nonce' is never repeated.+aeadDetached+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Message+    -> ByteString+    -- ^ AAD+    -> (ByteString,ByteString)+    -- ^ Tag, Ciphertext+aeadDetached (Key key) (Nonce nonce) msg aad =+  buildUnsafeByteString clen $ \pc ->+   fmap snd . buildUnsafeByteString' tlen $ \pt ->+    constByteStrings [key, msg, aad, nonce] $ \+      [(pk, _), (pm, _), (pa, _), (pn, _)] ->+          c_aead_detached pc pt nullPtr pm (fromIntegral mlen) pa (fromIntegral alen) nullPtr pn pk+  where mlen    = S.length msg+        alen    = S.length aad+        clen    = mlen+        tlen    = Bytes.aead_xchacha20poly1305_ietf_abytes++-- | Decrypts a message. Returns 'Nothing' if the keys and message do+-- not match.+aeadOpenDetached+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Tag+    -> ByteString+    -- ^ Ciphertext+    -> ByteString+    -- ^ AAD+    -> Maybe ByteString+    -- ^ Message+aeadOpenDetached (Key key) (Nonce nonce) tag cipher aad+    | S.length tag /= tlen = Nothing+    | otherwise =+  let (err, vec) = buildUnsafeByteString len $ \pm ->+        constByteStrings [key, tag, cipher, aad, nonce] $ \+          [(pk, _), (pt, _), (pc, _), (pa, _), (pn, _)] ->+            c_aead_open_detached pm nullPtr pc (fromIntegral len) pt pa (fromIntegral alen) pn pk+  in hush . handleErrno err $ vec+  where len    = S.length cipher+        alen   = S.length aad+        tlen   = Bytes.aead_xchacha20poly1305_ietf_abytes
src/Crypto/Saltine/Core/Auth.hs view
@@ -11,7 +11,7 @@ -- "Crypto.Saltine.Core.Auth" -- -- The 'auth' function authenticates a message 'ByteString' using a--- secret key The function returns an authenticator. The 'verify'+-- secret key. The function returns an authenticator. The 'verify' -- function checks if it's passed a correct authenticator of a message -- under the given secret key. --@@ -43,43 +43,20 @@   auth, verify   ) where -import           Crypto.Saltine.Class-import           Crypto.Saltine.Internal.Util-import qualified Crypto.Saltine.Internal.ByteSizes as Bytes--import           Control.Applicative-import           Foreign.C-import           Foreign.Ptr-import qualified Data.ByteString                   as S-import           Data.ByteString                     (ByteString)---- $types---- | An opaque 'auth' cryptographic key.-newtype Key           = Key ByteString deriving (Eq, Ord)---- | An opaque 'auth' authenticator.-newtype Authenticator = Au ByteString  deriving (Eq, Ord)--instance IsEncoding Key where-  decode v = if S.length v == Bytes.authKey-           then Just (Key v)-           else Nothing-  {-# INLINE decode #-}-  encode (Key v) = v-  {-# INLINE encode #-}+import Crypto.Saltine.Internal.Auth+            ( c_auth+            , c_auth_verify+            , Key(..)+            , Authenticator(..)+            )+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString) -instance IsEncoding Authenticator where-  decode v = if S.length v == Bytes.auth-           then Just (Au v)-           else Nothing-  {-# INLINE decode #-}-  encode (Au v) = v-  {-# INLINE encode #-}+import qualified Crypto.Saltine.Internal.Auth as Bytes  -- | Creates a random key of the correct size for 'auth' and 'verify'. newKey :: IO Key-newKey = Key <$> randomByteString Bytes.authKey+newKey = Key <$> randomByteString Bytes.auth_keybytes  -- | Computes an keyed authenticator 'ByteString' from a message. It -- is infeasible to forge these authenticators without the key, even@@ -90,7 +67,7 @@      -- ^ Message      -> Authenticator auth (Key key) msg =-  Au . snd . buildUnsafeByteString Bytes.auth $ \pa ->+  Au . snd . buildUnsafeByteString Bytes.auth_bytes $ \pa ->     constByteStrings [key, msg] $ \[(pk, _), (pm, mlen)] ->     c_auth pa pm (fromIntegral mlen) pk @@ -105,29 +82,3 @@ verify (Key key) (Au a) msg =   unsafeDidSucceed $ constByteStrings [key, msg, a] $ \[(pk, _), (pm, mlen), (pa, _)] ->     return $ c_auth_verify pa pm (fromIntegral mlen) pk--foreign import ccall "crypto_auth"-  c_auth :: Ptr CChar-         -- ^ Authenticator output buffer-         -> Ptr CChar-         -- ^ Constant message buffer-         -> CULLong-         -- ^ Length of message buffer-         -> Ptr CChar-         -- ^ Constant key buffer-         -> IO CInt-         -- ^ Always 0---- | We don't even include this in the IO monad since all of the--- buffers are constant.-foreign import ccall "crypto_auth_verify"-  c_auth_verify :: Ptr CChar-                -- ^ Constant authenticator buffer-                -> Ptr CChar-                -- ^ Constant message buffer-                -> CULLong-                -- ^ Length of message buffer-                -> Ptr CChar-                -- ^ Constant key buffer-                -> CInt-                -- ^ Success if 0, failure if -1
src/Crypto/Saltine/Core/Box.hs view
@@ -78,96 +78,52 @@ -- -- This is version 2010.08.30 of the box.html web page. module Crypto.Saltine.Core.Box (-  SecretKey, PublicKey, Keypair, CombinedKey, Nonce,+  SecretKey, PublicKey, Keypair(..), CombinedKey, Nonce,   newKeypair, beforeNM, newNonce,   box, boxOpen,   boxAfterNM, boxOpenAfterNM,   boxSeal, boxSealOpen   ) where -import           Crypto.Saltine.Class-import           Crypto.Saltine.Internal.Util-import qualified Crypto.Saltine.Internal.ByteSizes as Bytes--import           Control.Applicative-import           Foreign.C-import           Foreign.Ptr-import qualified Data.ByteString                   as S-import           Data.ByteString (ByteString)----- $types---- | An opaque 'box' cryptographic secret key.-newtype SecretKey = SK ByteString deriving (Eq, Ord)--instance IsEncoding SecretKey where-  decode v = if S.length v == Bytes.boxSK-           then Just (SK v)-           else Nothing-  {-# INLINE decode #-}-  encode (SK v) = v-  {-# INLINE encode #-}---- | An opaque 'box' cryptographic public key.-newtype PublicKey = PK ByteString deriving (Eq, Ord)--instance IsEncoding PublicKey where-  decode v = if S.length v == Bytes.boxPK-           then Just (PK v)-           else Nothing-  {-# INLINE decode #-}-  encode (PK v) = v-  {-# INLINE encode #-}---- | A convenience type for keypairs-type Keypair = (SecretKey, PublicKey)---- | An opaque 'boxAfterNM' cryptographic combined key.-newtype CombinedKey = CK ByteString deriving (Eq, Ord)--instance IsEncoding CombinedKey where-  decode v = if S.length v == Bytes.boxBeforeNM-           then Just (CK v)-           else Nothing-  {-# INLINE decode #-}-  encode (CK v) = v-  {-# INLINE encode #-}---- | An opaque 'box' nonce.-newtype Nonce = Nonce ByteString deriving (Eq, Ord)--instance IsEncoding Nonce where-  decode v = if S.length v == Bytes.boxNonce-           then Just (Nonce v)-           else Nothing-  {-# INLINE decode #-}-  encode (Nonce v) = v-  {-# INLINE encode #-}+import Crypto.Saltine.Internal.Box+            ( c_box_keypair+            , c_box_easy+            , c_box_open_easy+            , c_box_beforenm+            , c_box_easy_afternm+            , c_box_open_easy_afternm+            , c_box_seal, c_box_seal_open+            , SecretKey(..)+            , PublicKey(..)+            , Keypair(..)+            , CombinedKey(..)+            , Nonce(..)+            )+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString) -instance IsNonce Nonce where-  zero            = Nonce (S.replicate Bytes.boxNonce 0)-  nudge (Nonce n) = Nonce (nudgeBS n)+import qualified Crypto.Saltine.Internal.Box as Bytes+import qualified Data.ByteString             as S  -- | Randomly generates a secret key and a corresponding public key. newKeypair :: IO Keypair newKeypair = do   -- This is a little bizarre and a likely source of errors.   -- _err ought to always be 0.-  ((_err, sk), pk) <- buildUnsafeByteString' Bytes.boxPK $ \pkbuf ->-    buildUnsafeByteString' Bytes.boxSK $ \skbuf ->+  ((_err, sk), pk) <- buildUnsafeByteString' Bytes.box_publickeybytes $ \pkbuf ->+    buildUnsafeByteString' Bytes.box_secretkeybytes $ \skbuf ->       c_box_keypair pkbuf skbuf-  return (SK sk, PK pk)+  return $ Keypair (SK sk) (PK pk)  -- | Randomly generates a nonce for usage with 'box' and 'boxOpen'. newNonce :: IO Nonce-newNonce = Nonce <$> randomByteString Bytes.boxNonce+newNonce = Nonce <$> randomByteString Bytes.box_noncebytes  -- | Build a 'CombinedKey' for sending from 'SecretKey' to -- 'PublicKey'. This is a precomputation step which can accelerate -- later encryption calls. beforeNM :: SecretKey -> PublicKey -> CombinedKey-beforeNM (SK sk) (PK pk) = CK $ snd $ buildUnsafeByteString Bytes.boxBeforeNM $ \ckbuf ->+beforeNM (SK sk) (PK pk) = CK $ snd $ buildUnsafeByteString Bytes.box_beforenmbytes $ \ckbuf ->   constByteStrings [pk, sk] $ \[(ppk, _), (psk, _)] ->     c_box_beforenm ckbuf ppk psk @@ -188,7 +144,7 @@       [(ppk, _), (psk, _), (pm, _), (pn, _)] ->         c_box_easy pc pm (fromIntegral msgLen) pn ppk psk   where-    bufSize = S.length msg + Bytes.boxMac+    bufSize = S.length msg + Bytes.box_macbytes     msgLen  = S.length msg  -- | Decrypts a message sent from the owner of the public key. They@@ -199,16 +155,16 @@         -- ^ Ciphertext (incl. authentication tag)         -> Maybe ByteString         -- ^ Message-boxOpen (PK pk) (SK sk) (Nonce nonce) cipher =+boxOpen (PK pk) (SK sk) (Nonce nonce) cipher = do+  let msgLen = S.length cipher+  bufSize <- msgLen `safeSubtract` Bytes.box_macbytes   let (err, vec) = buildUnsafeByteString bufSize $ \pm ->         constByteStrings [pk, sk, cipher, nonce] $ \           [(ppk, _), (psk, _), (pc, _), (pn, _)] ->             c_box_open_easy pm pc (fromIntegral msgLen) pn ppk psk-  in hush . handleErrno err $ vec-  where-    bufSize = S.length cipher - Bytes.boxMac-    msgLen  = S.length cipher+  hush . handleErrno err $ vec + -- | 'box' using a 'CombinedKey' and thus faster. boxAfterNM :: CombinedKey            -> Nonce@@ -222,7 +178,7 @@       [(pck, _), (pm, _), (pn, _)] ->         c_box_easy_afternm pc pm (fromIntegral msgLen) pn pck   where-    bufSize = S.length msg + Bytes.boxMac+    bufSize = S.length msg + Bytes.box_macbytes     msgLen  = S.length msg  -- | 'boxOpen' using a 'CombinedKey' and is thus faster.@@ -232,26 +188,27 @@                -- ^ Ciphertext (incl. authentication tag)                -> Maybe ByteString                -- ^ Message-boxOpenAfterNM (CK ck) (Nonce nonce) cipher =+boxOpenAfterNM (CK ck) (Nonce nonce) cipher = do+  let msgLen = S.length cipher+  bufSize <- msgLen `safeSubtract` Bytes.box_macbytes   let (err, vec) = buildUnsafeByteString bufSize $ \pm ->         constByteStrings [ck, cipher, nonce] $ \           [(pck, _), (pc, _), (pn, _)] ->             c_box_open_easy_afternm pm pc (fromIntegral msgLen) pn pck-  in hush . handleErrno err $ vec-  where-    bufSize = S.length cipher - Bytes.boxMac-    msgLen  = S.length cipher+  hush . handleErrno err $ vec   -- | Encrypts a message for sending to the owner of the public -- key. The message is unauthenticated, but permits integrity checking.+-- This function is non-deterministic, it uses newly created ephemeral keys internally,+-- and thus in IO. boxSeal :: PublicKey -> ByteString -> IO ByteString boxSeal (PK pk) msg = fmap snd . buildUnsafeByteString' bufSize $ \pc ->     constByteStrings [pk, msg] $ \       [(ppk, _), (pm, _)] ->         c_box_seal pc pm (fromIntegral msgLen) ppk   where-    bufSize = S.length msg + Bytes.sealedBox+    bufSize = S.length msg + Bytes.box_sealbytes     msgLen  = S.length msg  -- | Decrypts a sealed box message. The message must have been@@ -264,126 +221,11 @@             -- ^ Ciphertext             -> Maybe ByteString             -- ^ Message-boxSealOpen (PK pk) (SK sk) cipher =+boxSealOpen (PK pk) (SK sk) cipher = do+  let msgLen = S.length cipher+  bufSize <- msgLen `safeSubtract` Bytes.box_sealbytes   let (err, vec) = buildUnsafeByteString bufSize $ \pm ->         constByteStrings [pk, sk, cipher] $ \           [(ppk, _), (psk, _), (pc, _)] ->           c_box_seal_open pm pc (fromIntegral msgLen) ppk psk-  in hush . handleErrno err $ vec-  where-    bufSize = S.length cipher - Bytes.sealedBox-    msgLen  = S.length cipher----- | Should always return a 0.-foreign import ccall "crypto_box_keypair"-  c_box_keypair :: Ptr CChar-                -- ^ Public key-                -> Ptr CChar-                -- ^ Secret key-                -> IO CInt-                -- ^ Always 0---- | The secretbox C API uses C strings.-foreign import ccall "crypto_box_easy"-  c_box_easy :: Ptr CChar-             -- ^ Cipher output buffer-             -> Ptr CChar-             -- ^ Constant message input buffer-             -> CULLong-             -- ^ Length of message input buffer-             -> Ptr CChar-             -- ^ Constant nonce buffer-             -> Ptr CChar-             -- ^ Constant public key buffer-             -> Ptr CChar-             -- ^ Constant secret key buffer-             -> IO CInt-             -- ^ Always 0---- | The secretbox C API uses C strings.-foreign import ccall "crypto_box_open_easy"-  c_box_open_easy :: Ptr CChar-                  -- ^ Message output buffer-                  -> Ptr CChar-                  -- ^ Constant ciphertext input buffer-                  -> CULLong-                  -- ^ Length of message input buffer-                  -> Ptr CChar-                  -- ^ Constant nonce buffer-                  -> Ptr CChar-                  -- ^ Constant public key buffer-                  -> Ptr CChar-                  -- ^ Constant secret key buffer-                  -> IO CInt-                  -- ^ 0 for success, -1 for failure to verify---- | Single target key precompilation.-foreign import ccall "crypto_box_beforenm"-  c_box_beforenm :: Ptr CChar-                 -- ^ Combined key output buffer-                 -> Ptr CChar-                 -- ^ Constant public key buffer-                 -> Ptr CChar-                 -- ^ Constant secret key buffer-                 -> IO CInt-                 -- ^ Always 0---- | Precompiled key crypto box. Uses C strings.-foreign import ccall "crypto_box_easy_afternm"-  c_box_easy_afternm :: Ptr CChar-                     -- ^ Cipher output buffer-                     -> Ptr CChar-                     -- ^ Constant message input buffer-                     -> CULLong-                     -- ^ Length of message input buffer (incl. 0s)-                     -> Ptr CChar-                     -- ^ Constant nonce buffer-                     -> Ptr CChar-                     -- ^ Constant combined key buffer-                     -> IO CInt-                     -- ^ Always 0---- | The secretbox C API uses C strings.-foreign import ccall "crypto_box_open_easy_afternm"-  c_box_open_easy_afternm :: Ptr CChar-                          -- ^ Message output buffer-                          -> Ptr CChar-                          -- ^ Constant ciphertext input buffer-                          -> CULLong-                          -- ^ Length of message input buffer (incl. 0s)-                          -> Ptr CChar-                          -- ^ Constant nonce buffer-                          -> Ptr CChar-                          -- ^ Constant combined key buffer-                          -> IO CInt-                          -- ^ 0 for success, -1 for failure to verify----- | The sealedbox C API uses C strings.-foreign import ccall "crypto_box_seal"-  c_box_seal :: Ptr CChar-             -- ^ Cipher output buffer-             -> Ptr CChar-             -- ^ Constant message input buffer-             -> CULLong-             -- ^ Length of message input buffer-             -> Ptr CChar-             -- ^ Constant public key buffer-             -> IO CInt-             -- ^ Always 0---- | The sealedbox C API uses C strings.-foreign import ccall "crypto_box_seal_open"-  c_box_seal_open :: Ptr CChar-                  -- ^ Message output buffer-                  -> Ptr CChar-                  -- ^ Constant ciphertext input buffer-                  -> CULLong-                  -- ^ Length of message input buffer-                  -> Ptr CChar-                  -- ^ Constant public key buffer-                  -> Ptr CChar-                  -- ^ Constant secret key buffer-                  -> IO CInt-                  -- ^ 0 for success, -1 for failure to decrypt+  hush . handleErrno err $ vec
src/Crypto/Saltine/Core/Hash.hs view
@@ -1,4 +1,4 @@--- |+ -- | -- Module      : Crypto.Saltine.Core.Hash -- Copyright   : (c) Joseph Abrahamson 2013 -- License     : MIT@@ -34,27 +34,45 @@ -- alternatives that inspire satisfactory levels of confidence. One -- can hope that NIST's SHA-3 competition will improve the situation. ----- Sodium includes an implementation of the Blake2 hash--- (<https://blake2.net/>) but since this is not standard NaCl nor was--- Blake2 selected to be SHA-3 the library doesn't bind it.+-- Sodium includes an implementation of the Blake2b hash function+-- (<https://blake2.net/>) and is bound here as the 'generichash'+-- function. -- -- This is version 2010.08.30 of the hash.html web page. Information -- about SipHash has been added. module Crypto.Saltine.Core.Hash (+  -- * Documentation   ShorthashKey,   hash,-  shorthash, newShorthashKey+  shorthash, newShorthashKey,+  GenerichashKey,+  newGenerichashKey,+  GenerichashOutLen,+  generichashOutLen, generichash,++  -- * Advanced: SHA-2+  --+  -- | The SHA-256 function is provided for interoperability with+  -- other applications. If you are looking for a generic hash+  -- function and not specifically SHA-2, using 'generichash'+  -- (BLAKE2b) might be a better choice.+  sha256   ) where -import           Crypto.Saltine.Class-import           Crypto.Saltine.Internal.Util-import qualified Crypto.Saltine.Internal.ByteSizes as Bytes+import Crypto.Saltine.Internal.Hash+            ( c_hash+            , c_generichash+            , c_hash_sha256+            , shorthash+            , ShorthashKey(..)+            , GenerichashKey(..)+            , GenerichashOutLen(..)+            )+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString) -import           Control.Applicative-import           Foreign.C-import           Foreign.Ptr-import qualified Data.ByteString as S-import           Data.ByteString (ByteString)+import qualified Crypto.Saltine.Internal.Hash as Bytes+import qualified Data.ByteString              as S  -- | Computes a cryptographically collision-resistant hash making -- @hash m == hash m' ==> m == m'@ highly likely even when under@@ -63,52 +81,38 @@      -- ^ Message      -> ByteString      -- ^ Hash-hash m = snd . buildUnsafeByteString Bytes.hash $ \ph ->+hash m = snd . buildUnsafeByteString Bytes.hash_bytes $ \ph ->   constByteStrings [m] $ \[(pm, _)] -> c_hash ph pm (fromIntegral $ S.length m) --- | An opaque 'shorthash' cryptographic secret key.-newtype ShorthashKey = ShK ByteString deriving (Eq, Ord)--instance IsEncoding ShorthashKey where-  decode v = if S.length v == Bytes.shorthashKey-           then Just (ShK v)-           else Nothing-  {-# INLINE decode #-}-  encode (ShK v) = v-  {-# INLINE encode #-}- -- | Randomly generates a new key for 'shorthash'. newShorthashKey :: IO ShorthashKey-newShorthashKey = ShK <$> randomByteString Bytes.shorthashKey+newShorthashKey = ShK <$> randomByteString Bytes.shorthash_keybytes --- | Computes a very short, fast keyed hash.-shorthash :: ShorthashKey-          -> ByteString-          -- ^ Message-          -> ByteString-          -- ^ Hash-shorthash (ShK k) m = snd . buildUnsafeByteString Bytes.shorthash $ \ph ->-  constByteStrings [k, m] $ \[(pk, _), (pm, _)] ->-    c_shorthash ph pm (fromIntegral $ S.length m) pk+-- | Randomly generates a new key for 'generichash' of the given length.+newGenerichashKey :: Int -> IO (Maybe GenerichashKey)+newGenerichashKey n = if n >= 0 && n <= Bytes.generichash_keybytes_max+                      then Just . GhK <$> randomByteString n+                      else return Nothing -foreign import ccall "crypto_hash"-  c_hash :: Ptr CChar-         -- ^ Output hash buffer-         -> Ptr CChar-         -- ^ Constant message buffer-         -> CULLong-         -- ^ Constant message buffer length-         -> IO CInt-         -- ^ Always 0+-- | Create a validated Generichash output length+generichashOutLen :: Int -> Maybe GenerichashOutLen+generichashOutLen n = if n > 0 && n <= Bytes.generichash_bytes_max+                      then Just $ GhOL $ fromIntegral n+                      else Nothing -foreign import ccall "crypto_shorthash"-  c_shorthash :: Ptr CChar-              -- ^ Output hash buffer-              -> Ptr CChar-              -- ^ Constant message buffer-              -> CULLong-              -- ^ Message buffer length-              -> Ptr CChar-              -- ^ Constant Key buffer-              -> IO CInt-              -- ^ Always 0+-- | Computes a generic, keyed hash.+generichash :: GenerichashKey+            -> ByteString+            -- ^ Message+            -> GenerichashOutLen+            -- ^ Desired output hash length+            -> ByteString+            -- ^ Hash+generichash (GhK k) m (GhOL outLen) = snd . buildUnsafeByteString outLen $ \ph ->+  constByteStrings [k, m] $ \[(pk, _), (pm, _)] ->+    c_generichash ph (fromIntegral outLen) pm (fromIntegral $ S.length m) pk (fromIntegral $ S.length k)++-- | Computes a SHA256 hash.+sha256 :: ByteString -> ByteString+sha256 m = snd . buildUnsafeByteString Bytes.hash_sha256_bytes $ \ph ->+  constByteStrings [m] $ \[(pm,_)] -> c_hash_sha256 ph pm (fromIntegral $ S.length m)
src/Crypto/Saltine/Core/OneTimeAuth.hs view
@@ -39,43 +39,21 @@   auth, verify   ) where -import           Crypto.Saltine.Class-import           Crypto.Saltine.Internal.Util-import qualified Crypto.Saltine.Internal.ByteSizes as Bytes+import Crypto.Saltine.Internal.OneTimeAuth+            ( c_onetimeauth+            , c_onetimeauth_verify+            , Key(..)+            , Authenticator(..)+            )+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString) -import           Control.Applicative-import           Foreign.C-import           Foreign.Ptr+import qualified Crypto.Saltine.Internal.OneTimeAuth as Bytes import qualified Data.ByteString                   as S-import           Data.ByteString                     (ByteString) --- $types---- | An opaque 'auth' cryptographic key.-newtype Key           = Key ByteString deriving (Eq, Ord)---- | An opaque 'auth' authenticator.-newtype Authenticator = Au ByteString  deriving (Eq, Ord)--instance IsEncoding Key where-  decode v = if S.length v == Bytes.onetimeKey-           then Just (Key v)-           else Nothing-  {-# INLINE decode #-}-  encode (Key v) = v-  {-# INLINE encode #-}--instance IsEncoding Authenticator where-  decode v = if S.length v == Bytes.onetime-           then Just (Au v)-           else Nothing-  {-# INLINE decode #-}-  encode (Au v) = v-  {-# INLINE encode #-}- -- | Creates a random key of the correct size for 'auth' and 'verify'. newKey :: IO Key-newKey = Key <$> randomByteString Bytes.onetimeKey+newKey = Key <$> randomByteString Bytes.onetimeauth_keybytes  -- | Builds a keyed 'Authenticator' for a message. This -- 'Authenticator' is /impossible/ to forge so long as the 'Key' is@@ -85,7 +63,7 @@      -- ^ Message      -> Authenticator auth (Key key) msg =-  Au . snd . buildUnsafeByteString Bytes.onetime $ \pa ->+  Au . snd . buildUnsafeByteString Bytes.onetimeauth_bytes $ \pa ->     constByteStrings [key, msg] $ \[(pk, _), (pm, _)] ->       c_onetimeauth pa pm (fromIntegral $ S.length msg) pk @@ -100,29 +78,3 @@   unsafeDidSucceed $ constByteStrings [key, msg, a] $ \     [(pk, _), (pm, _), (pa, _)] ->       return $ c_onetimeauth_verify pa pm (fromIntegral $ S.length msg) pk--foreign import ccall "crypto_onetimeauth"-  c_onetimeauth :: Ptr CChar-                -- ^ Authenticator output buffer-                -> Ptr CChar-                -- ^ Constant message buffer-                -> CULLong-                -- ^ Length of message buffer-                -> Ptr CChar-                -- ^ Constant key buffer-                -> IO CInt-                -- ^ Always 0---- | We don't even include this in the IO monad since all of the--- buffers are constant.-foreign import ccall "crypto_onetimeauth_verify"-  c_onetimeauth_verify :: Ptr CChar-                       -- ^ Constant authenticator buffer-                       -> Ptr CChar-                       -- ^ Constant message buffer-                       -> CULLong-                       -- ^ Length of message buffer-                       -> Ptr CChar-                       -- ^ Constant key buffer-                       -> CInt-                       -- ^ Success if 0, failure if -1
+ src/Crypto/Saltine/Core/Password.hs view
@@ -0,0 +1,247 @@+-- |+-- Module      : Crypto.Saltine.Core.Password+-- Description : Argon2 password hash+-- Copyright   : (c) Promethea Raschke 2018+--                   Max Amanshauser 2021+-- License     : MIT+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable+--+-- Password hashing and key derivation+--+-- When in doubt, just use one of [ interactivePolicy, moderatePolicy, sensitivePolicy ],+-- but this module also allows you to fine-tune parameters for specific circumstances.+--+-- This module uses the @Text@ type for passwords, because this seems to be the only+-- reasonable way to get consistent encodings across locales and architectures, short of+-- letting users mess around with ByteStrings themselves.++module Crypto.Saltine.Core.Password+  ( Salt+  , newSalt++  , needsRehash++  , pwhashStr+  , pwhashStrVerify+  , pwhash++  , Policy(..)+  , interactivePolicy+  , moderatePolicy+  , sensitivePolicy++  , Opslimit+  , opslimit+  , getOpslimit++  , minOpslimit+  , maxOpslimit++  , opslimitInteractive+  , opslimitModerate+  , opslimitSensitive++  , Memlimit+  , memlimit+  , getMemlimit++  , minMemlimit+  , maxMemlimit++  , memlimitInteractive+  , memlimitModerate+  , memlimitSensitive++  , Algorithm+  , defaultAlgorithm+  ) where++import Crypto.Saltine.Internal.Util+import Crypto.Saltine.Internal.Password as I+import Data.ByteString (ByteString)+import Data.Text       (Text)+import Foreign.C+import System.IO.Unsafe++import qualified Crypto.Saltine.Internal.Password as Bytes+import qualified Data.Text as T+import qualified Data.Text.Encoding as TE+++newSalt :: IO Salt+newSalt = Salt <$> randomByteString Bytes.pwhash_saltbytes++-- | Hashes a password according to the policy+-- This function is non-deterministic and hence in IO.+-- Since this function may cause a huge amount of memory to be allocated, it will return+-- Nothing if the allocation failed and on any other error.+pwhashStr :: Text -> Policy -> IO (Maybe PasswordHash)+pwhashStr pw policy = do+  let (ops, mem, _alg) = unpackPolicy policy++  -- Hash is always ASCII, so no decoding needed+  fmap (fmap (PasswordHash . T.pack)) $ allocaBytes pwhash_strbytes $ \pp ->+    constByteStrings [TE.encodeUtf8 pw] $ \ [(ppw, ppwlen)] -> do+          ret <- c_pwhash_str pp ppw (fromIntegral ppwlen) (fromIntegral ops) (fromIntegral mem)++          case ret of+              0 -> Just <$> peekCAString pp+              _ -> pure Nothing++-- | Verifies that a certain password hash was constructed from the supplied password+pwhashStrVerify :: PasswordHash -> Text -> Bool+pwhashStrVerify (PasswordHash h) pw = unsafePerformIO $+  constByteStrings [TE.encodeUtf8 $ T.snoc h '\NUL', TE.encodeUtf8 pw] $ \[(ph, _), (ppw, ppwlen)] -> do+    res <- c_pwhash_str_verify ph ppw (fromIntegral ppwlen)+    pure (res == 0)++-- | Indicates whether a password needs to be rehashed, because the opslimit/memlimit parameters+-- used to hash the password are inconsistent with the supplied values.+-- Returns Nothing if the hash appears to be invalid.+-- Internally this function will always use the current DefaultAlgorithm and hence will give+-- undefined results if a different algorithm was used to hash the password.+needsRehash :: Opslimit -> Memlimit -> PasswordHash -> Maybe Bool+needsRehash (Opslimit ops) (Memlimit mem) (PasswordHash h) =+    unsafePerformIO $+        constByteStrings [TE.encodeUtf8 $ T.snoc h '\NUL'] $ \[(ph,_)] -> do+            res <- c_pwhash_str_needs_rehash ph (fromIntegral ops) (fromIntegral mem)+            pure $  if res == -1+                    then Nothing+                    else Just (res == 1)++-- | Derives a key of the specified length from a password using a salt according to the provided policy.+-- Since this function may cause a huge amount of memory to be allocated, it will return+-- Nothing if the allocation failed and on any other error.+pwhash :: Text -> Int -> Salt -> Policy -> Maybe ByteString+pwhash pw len (Salt salt) policy = do+  let (ops, mem, alg) = unpackPolicy policy++  let (e, hashed) =+        buildUnsafeByteString len $ \hbuf ->+          constByteStrings [TE.encodeUtf8 pw, salt] $ \[(ppw,ppwlen), (psalt,_)] ->+              c_pwhash+                    hbuf (fromIntegral len)+                    ppw (fromIntegral ppwlen)+                    psalt+                    (fromIntegral ops)+                    (fromIntegral mem)+                    (fromIntegral $ fromEnum alg)++  if e == -1+  then Nothing+  else Just hashed+++-- | Smart constructor for opslimit+opslimit :: Algorithm -> Int -> Maybe Opslimit+opslimit alg x+  | Opslimit x < minOpslimit alg = Nothing+  | Opslimit x > maxOpslimit alg = Nothing+  | otherwise = Just (Opslimit x)++opslimitInteractive :: Algorithm -> Opslimit+opslimitInteractive DefaultAlgorithm = Opslimit (fromIntegral Bytes.pwhash_opslimit_interactive)+opslimitInteractive Argon2i13        = Opslimit (fromIntegral Bytes.pwhash_argon2i_opslimit_interactive)+opslimitInteractive Argon2id13       = Opslimit (fromIntegral Bytes.pwhash_argon2id_opslimit_interactive)++opslimitModerate :: Algorithm -> Opslimit+opslimitModerate DefaultAlgorithm   = Opslimit (fromIntegral Bytes.pwhash_opslimit_moderate)+opslimitModerate Argon2i13          = Opslimit (fromIntegral Bytes.pwhash_argon2i_opslimit_moderate)+opslimitModerate Argon2id13         = Opslimit (fromIntegral Bytes.pwhash_argon2id_opslimit_moderate)++opslimitSensitive :: Algorithm -> Opslimit+opslimitSensitive DefaultAlgorithm  = Opslimit (fromIntegral Bytes.pwhash_opslimit_sensitive)+opslimitSensitive Argon2i13         = Opslimit (fromIntegral Bytes.pwhash_argon2i_opslimit_sensitive)+opslimitSensitive Argon2id13        = Opslimit (fromIntegral Bytes.pwhash_argon2id_opslimit_sensitive)+++-- | Smart constructor for memlimit+memlimit :: Algorithm -> Int -> Maybe Memlimit+memlimit alg x+  | Memlimit x < minMemlimit alg  = Nothing+  | Memlimit x > maxMemlimit alg= Nothing+  | otherwise = Just (Memlimit x)++memlimitInteractive :: Algorithm -> Memlimit+memlimitInteractive DefaultAlgorithm = Memlimit (fromIntegral Bytes.pwhash_memlimit_interactive)+memlimitInteractive Argon2i13        = Memlimit (fromIntegral Bytes.pwhash_argon2i_memlimit_interactive)+memlimitInteractive Argon2id13       = Memlimit (fromIntegral Bytes.pwhash_argon2id_memlimit_interactive)++memlimitModerate :: Algorithm -> Memlimit+memlimitModerate DefaultAlgorithm   = Memlimit (fromIntegral Bytes.pwhash_memlimit_moderate)+memlimitModerate Argon2i13          = Memlimit (fromIntegral Bytes.pwhash_argon2i_memlimit_moderate)+memlimitModerate Argon2id13         = Memlimit (fromIntegral Bytes.pwhash_argon2id_memlimit_moderate)++memlimitSensitive :: Algorithm -> Memlimit+memlimitSensitive DefaultAlgorithm  = Memlimit (fromIntegral Bytes.pwhash_memlimit_sensitive)+memlimitSensitive Argon2i13         = Memlimit (fromIntegral Bytes.pwhash_argon2i_memlimit_sensitive)+memlimitSensitive Argon2id13        = Memlimit (fromIntegral Bytes.pwhash_argon2id_memlimit_sensitive)++defaultAlgorithm :: Algorithm+defaultAlgorithm = DefaultAlgorithm+++-- | Get raw C types from a policy, suitable for passing to FFI functions+unpackPolicy :: Policy -> (CULLong, CSize, CInt)+unpackPolicy (Policy ops mem alg) =+  ( fromIntegral (getOpslimit ops)+  , fromIntegral (getMemlimit mem)+  , algorithm alg+  )+++{-+Fast policy suitable for low-powered devices++Takes approximately 0.1 seconds on a typical desktop computer+and requires 64 MiB of dedicated RAM+-}+interactivePolicy :: Policy+interactivePolicy = Policy (opslimitInteractive defaultAlgorithm)+                           (memlimitInteractive defaultAlgorithm)+                           defaultAlgorithm++{-|+Moderate policy with a balance of speed and security++Takes approximately 1 second on a typical desktop computer+and requires 256 MiB of dedicated RAM+-}+moderatePolicy :: Policy+moderatePolicy = Policy (opslimitModerate defaultAlgorithm)+                        (memlimitModerate defaultAlgorithm)+                        defaultAlgorithm++{-|+High-security policy designed to make attacking the password extremely expensive++Takes several seconds on a typical desktop computer+and requires 1024 MiB of dedicated RAM+-}+sensitivePolicy :: Policy+sensitivePolicy = Policy (opslimitSensitive defaultAlgorithm)+                         (memlimitSensitive defaultAlgorithm)+                         defaultAlgorithm+++minOpslimit :: Algorithm -> Opslimit+minOpslimit DefaultAlgorithm = Opslimit $ fromIntegral Bytes.pwhash_opslimit_min+minOpslimit Argon2i13        = Opslimit $ fromIntegral Bytes.pwhash_argon2i_opslimit_min+minOpslimit Argon2id13       = Opslimit $ fromIntegral Bytes.pwhash_argon2id_opslimit_min++maxOpslimit :: Algorithm -> Opslimit+maxOpslimit DefaultAlgorithm = Opslimit $ fromIntegral Bytes.pwhash_opslimit_max+maxOpslimit Argon2i13        = Opslimit $ fromIntegral Bytes.pwhash_argon2i_opslimit_max+maxOpslimit Argon2id13       = Opslimit $ fromIntegral Bytes.pwhash_argon2id_opslimit_max++minMemlimit :: Algorithm -> Memlimit+minMemlimit DefaultAlgorithm = Memlimit $ fromIntegral Bytes.pwhash_memlimit_min+minMemlimit Argon2i13        = Memlimit $ fromIntegral Bytes.pwhash_argon2i_memlimit_min+minMemlimit Argon2id13       = Memlimit $ fromIntegral Bytes.pwhash_argon2id_memlimit_min++maxMemlimit :: Algorithm -> Memlimit+maxMemlimit DefaultAlgorithm = Memlimit $ fromIntegral Bytes.pwhash_memlimit_max+maxMemlimit Argon2i13        = Memlimit $ fromIntegral Bytes.pwhash_argon2i_memlimit_max+maxMemlimit Argon2id13       = Memlimit $ fromIntegral Bytes.pwhash_argon2id_memlimit_max
src/Crypto/Saltine/Core/ScalarMult.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving, DeriveGeneric #-}+ -- | -- Module      : Crypto.Saltine.Core.ScalarMult -- Copyright   : (c) Joseph Abrahamson 2013@@ -55,63 +57,23 @@   mult, multBase   ) where -import           Crypto.Saltine.Class-import           Crypto.Saltine.Internal.Util-import qualified Crypto.Saltine.Internal.ByteSizes as Bytes+import Crypto.Saltine.Internal.Util+import Crypto.Saltine.Internal.ScalarMult+            ( c_scalarmult+            , c_scalarmult_base+            , GroupElement(..)+            , Scalar(..)+            ) -import           Foreign.C-import           Foreign.Ptr-import qualified Data.ByteString                   as S-import           Data.ByteString                     (ByteString)+import qualified Crypto.Saltine.Internal.ScalarMult as Bytes --- $types --- | A group element.-newtype GroupElement = GE ByteString deriving (Eq)---- | A scalar integer.-newtype Scalar       = Sc ByteString deriving (Eq)--instance IsEncoding GroupElement where-  decode v = if S.length v == Bytes.mult-           then Just (GE v)-           else Nothing-  {-# INLINE decode #-}-  encode (GE v) = v-  {-# INLINE encode #-}--instance IsEncoding Scalar where-  decode v = if S.length v == Bytes.multScalar-           then Just (Sc v)-           else Nothing-  {-# INLINE decode #-}-  encode (Sc v) = v-  {-# INLINE encode #-}- mult :: Scalar -> GroupElement -> GroupElement-mult (Sc n) (GE p) = GE . snd . buildUnsafeByteString Bytes.mult $ \pq ->+mult (Sc n) (GE p) = GE . snd . buildUnsafeByteString Bytes.scalarmult_bytes $ \pq ->   constByteStrings [n, p] $ \[(pn, _), (pp, _)] ->     c_scalarmult pq pn pp  multBase :: Scalar -> GroupElement-multBase (Sc n) = GE . snd . buildUnsafeByteString Bytes.mult $ \pq ->+multBase (Sc n) = GE . snd . buildUnsafeByteString Bytes.scalarmult_bytes $ \pq ->   constByteStrings [n] $ \[(pn, _)] ->     c_scalarmult_base pq pn--foreign import ccall "crypto_scalarmult"-  c_scalarmult :: Ptr CChar-               -- ^ Output group element buffer-               -> Ptr CChar-               -- ^ Input integer buffer-               -> Ptr CChar-               -- ^ Input group element buffer-               -> IO CInt-               -- ^ Always 0--foreign import ccall "crypto_scalarmult_base"-  c_scalarmult_base :: Ptr CChar-                    -- ^ Output group element buffer-                    -> Ptr CChar-                    -- ^ Input integer buffer-                    -> IO CInt-                    -- ^ Always 0
src/Crypto/Saltine/Core/SecretBox.hs view
@@ -3,7 +3,7 @@ -- Copyright   : (c) Joseph Abrahamson 2013 -- License     : MIT ----- Maintainer  : me@jspha.com+-- Maintainer  : max@lambdalifting.org -- Stability   : experimental -- Portability : non-portable --@@ -38,120 +38,105 @@ -- -- This is version 2010.08.30 of the secretbox.html web page. module Crypto.Saltine.Core.SecretBox (-  Key, Nonce,+  Key, Nonce, Authenticator,   secretbox, secretboxOpen,   secretboxDetached, secretboxOpenDetached,   newKey, newNonce   ) where -import           Crypto.Saltine.Class-import           Crypto.Saltine.Internal.Util-import qualified Crypto.Saltine.Internal.ByteSizes as Bytes+import Crypto.Saltine.Internal.SecretBox+            ( c_secretbox+            , c_secretbox_detached+            , c_secretbox_open+            , c_secretbox_open_detached+            , Key(..)+            , Nonce(..)+            , Authenticator(..)+            )+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString) -import           Control.Applicative-import           Foreign.C-import           Foreign.Ptr+import qualified Crypto.Saltine.Internal.SecretBox as Bytes import qualified Data.ByteString                   as S-import           Data.ByteString                     (ByteString) --- $types---- | An opaque 'secretbox' cryptographic key.-newtype Key = Key ByteString deriving (Eq, Ord)--instance IsEncoding Key where-  decode v = if S.length v == Bytes.secretBoxKey-           then Just (Key v)-           else Nothing-  {-# INLINE decode #-}-  encode (Key v) = v-  {-# INLINE encode #-}---- | An opaque 'secretbox' nonce.-newtype Nonce = Nonce ByteString deriving (Eq, Ord)--instance IsEncoding Nonce where-  decode v = if S.length v == Bytes.secretBoxNonce-           then Just (Nonce v)-           else Nothing-  {-# INLINE decode #-}-  encode (Nonce v) = v-  {-# INLINE encode #-}--instance IsNonce Nonce where-  zero            = Nonce (S.replicate Bytes.secretBoxNonce 0)-  nudge (Nonce n) = Nonce (nudgeBS n)- -- | Creates a random key of the correct size for 'secretbox'. newKey :: IO Key-newKey = Key <$> randomByteString Bytes.secretBoxKey+newKey = Key <$> randomByteString Bytes.secretbox_keybytes  -- | Creates a random nonce of the correct size for 'secretbox'. newNonce :: IO Nonce-newNonce = Nonce <$> randomByteString Bytes.secretBoxNonce+newNonce = Nonce <$> randomByteString Bytes.secretbox_noncebytes  -- | Encrypts a message. It is infeasible for an attacker to decrypt -- the message so long as the 'Nonce' is never repeated.-secretbox :: Key -> Nonce-          -> ByteString-          -- ^ Message-          -> ByteString-          -- ^ Ciphertext+secretbox+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Message+    -> ByteString+    -- ^ Ciphertext secretbox (Key key) (Nonce nonce) msg =   unpad' . snd . buildUnsafeByteString len $ \pc ->     constByteStrings [key, pad' msg, nonce] $ \       [(pk, _), (pm, _), (pn, _)] ->         c_secretbox pc pm (fromIntegral len) pn pk-  where len    = S.length msg + Bytes.secretBoxZero-        pad'   = pad Bytes.secretBoxZero-        unpad' = unpad Bytes.secretBoxBoxZero+  where len    = S.length msg + Bytes.secretbox_zerobytes+        pad'   = pad Bytes.secretbox_zerobytes+        unpad' = unpad Bytes.secretbox_boxzerobytes  -- | Encrypts a message. In contrast with 'secretbox', the result is not -- serialized as one element and instead provided as an authentication tag and -- ciphertext.-secretboxDetached :: Key -> Nonce-          -> ByteString-          -- ^ Message-          -> (ByteString,ByteString)-          -- ^ (Authentication Tag, Ciphertext)+secretboxDetached+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Message+    -> (Authenticator,ByteString)+    -- ^ (Authentication Tag, Ciphertext) secretboxDetached (Key key) (Nonce nonce) msg =   buildUnsafeByteString ctLen $ \pc ->-   fmap snd . buildUnsafeByteString' tagLen $ \ptag ->-    constByteStrings [key, msg, nonce] $ \-      [(pk, _), (pmsg, _), (pn, _)] ->-        c_secretbox_detached pc ptag pmsg (fromIntegral ptLen) pn pk+    fmap (Au . snd) . buildUnsafeByteString' tagLen $ \ptag ->+      constByteStrings [key, msg, nonce] $ \+        [(pk, _), (pmsg, _), (pn, _)] ->+          c_secretbox_detached pc ptag pmsg (fromIntegral ptLen) pn pk   where ctLen  = ptLen         ptLen  = S.length msg-        tagLen = Bytes.secretBoxMac+        tagLen = Bytes.secretbox_macbytes  -- | Decrypts a message. Returns 'Nothing' if the keys and message do -- not match.-secretboxOpen :: Key -> Nonce -                 -> ByteString-                 -- ^ Ciphertext-                 -> Maybe ByteString-                 -- ^ Message+secretboxOpen+    :: Key+    -> Nonce+    -> ByteString+    -- ^ Ciphertext+    -> Maybe ByteString+    -- ^ Message secretboxOpen (Key key) (Nonce nonce) cipher =   let (err, vec) = buildUnsafeByteString len $ \pm ->         constByteStrings [key, pad' cipher, nonce] $ \           [(pk, _), (pc, _), (pn, _)] ->             c_secretbox_open pm pc (fromIntegral len) pn pk   in hush . handleErrno err $ unpad' vec-  where len    = S.length cipher + Bytes.secretBoxBoxZero-        pad'   = pad Bytes.secretBoxBoxZero-        unpad' = unpad Bytes.secretBoxZero+  where len    = S.length cipher + Bytes.secretbox_boxzerobytes+        pad'   = pad Bytes.secretbox_boxzerobytes+        unpad' = unpad Bytes.secretbox_zerobytes  -- | Decrypts a message. Returns 'Nothing' if the keys and message do -- not match.-secretboxOpenDetached :: Key -> Nonce-                 -> ByteString-                 -- ^ Auth Tag-                 -> ByteString-                 -- ^ Ciphertext-                 -> Maybe ByteString-                 -- ^ Message-secretboxOpenDetached (Key key) (Nonce nonce) tag cipher-    | S.length tag /= Bytes.secretBoxMac = Nothing+secretboxOpenDetached+    :: Key+    -> Nonce+    -> Authenticator+    -- ^ Auth Tag+    -> ByteString+    -- ^ Ciphertext+    -> Maybe ByteString+    -- ^ Message+secretboxOpenDetached (Key key) (Nonce nonce) (Au tag) cipher+    | S.length tag /= Bytes.secretbox_macbytes = Nothing     | otherwise =   let (err, vec) = buildUnsafeByteString len $ \pm ->         constByteStrings [key, cipher, tag, nonce] $ \@@ -159,67 +144,3 @@             c_secretbox_open_detached pm pc pt (fromIntegral len) pn pk   in hush . handleErrno err $ vec   where len    = S.length cipher---- | The secretbox C API uses 0-padded C strings. Always returns 0.-foreign import ccall "crypto_secretbox"-  c_secretbox :: Ptr CChar-              -- ^ Cipher 0-padded output buffer-              -> Ptr CChar-              -- ^ Constant 0-padded message input buffer-              -> CULLong-              -- ^ Length of message input buffer (incl. 0s)-              -> Ptr CChar-              -- ^ Constant nonce buffer-              -> Ptr CChar-              -- ^ Constant key buffer-              -> IO CInt---- | The secretbox_detached C API uses C strings. Always returns 0.-foreign import ccall "crypto_secretbox_detached"-  c_secretbox_detached-              :: Ptr CChar-              -- ^ Ciphertext output buffer-              -> Ptr CChar-              -- ^ Authentication tag output buffer-              -> Ptr CChar-              -- ^ Constant message input buffer-              -> CULLong-              -- ^ Length of message input buffer (incl. 0s)-              -> Ptr CChar-              -- ^ Constant nonce buffer-              -> Ptr CChar-              -- ^ Constant key buffer-              -> IO CInt---- | The secretbox C API uses 0-padded C strings. Returns 0 if--- successful or -1 if verification failed.-foreign import ccall "crypto_secretbox_open"-  c_secretbox_open :: Ptr CChar-                   -- ^ Message 0-padded output buffer-                   -> Ptr CChar-                   -- ^ Constant 0-padded message input buffer-                   -> CULLong-                   -- ^ Length of message input buffer (incl. 0s)-                   -> Ptr CChar-                   -- ^ Constant nonce buffer-                   -> Ptr CChar-                   -- ^ Constant key buffer-                   -> IO CInt---- | The secretbox C API uses C strings. Returns 0 if--- successful or -1 if verification failed.-foreign import ccall "crypto_secretbox_open_detached"-  c_secretbox_open_detached-                   :: Ptr CChar-                   -- ^ Message output buffer-                   -> Ptr CChar-                   -- ^ Constant ciphertext input buffer-                   -> Ptr CChar-                   -- ^ Constant auth tag input buffer-                   -> CULLong-                   -- ^ Length of ciphertext input buffer-                   -> Ptr CChar-                   -- ^ Constant nonce buffer-                   -> Ptr CChar-                   -- ^ Constant key buffer-                   -> IO CInt
src/Crypto/Saltine/Core/Sign.hs view
@@ -1,3 +1,4 @@+ -- | -- Module      : Crypto.Saltine.Core.Sign -- Copyright   : (c) Joseph Abrahamson 2013@@ -26,50 +27,43 @@ -- This is current information as of 2013 June 6.  module Crypto.Saltine.Core.Sign (-  SecretKey, PublicKey, Keypair,+  -- * Documentation+  SecretKey, PublicKey, Keypair(..), Signature,   newKeypair,   sign, signOpen,-  signDetached, signVerifyDetached-  ) where--import           Crypto.Saltine.Class-import           Crypto.Saltine.Internal.Util-import qualified Crypto.Saltine.Internal.ByteSizes as Bytes--import           Foreign.C-import           Foreign.Ptr-import           Foreign.Marshal.Alloc-import           Foreign.Storable-import           System.IO.Unsafe-import qualified Data.ByteString                   as S-import           Data.ByteString                     (ByteString)---- $types---- | An opaque 'box' cryptographic secret key.-newtype SecretKey = SK ByteString deriving (Eq, Ord)--instance IsEncoding SecretKey where-  decode v = if S.length v == Bytes.signSK-           then Just (SK v)-           else Nothing-  {-# INLINE decode #-}-  encode (SK v) = v-  {-# INLINE encode #-}+  signDetached, signVerifyDetached, --- | An opaque 'box' cryptographic public key.-newtype PublicKey = PK ByteString deriving (Eq, Ord)+  -- * Advanced: Ed25519 to Curve25519+  --+  -- | Ed25519 keys can be converted to X25519 keys, so that the same+  -- key pair can be used both for authenticated encryption+  -- ("Crypto.Saltine.Core.Box") and for signatures+  -- ("Crypto.Saltine.Core.Sign").+  signPublicKeyToScalarMult, signSecretKeyToScalarMult+  ) where -instance IsEncoding PublicKey where-  decode v = if S.length v == Bytes.signPK-           then Just (PK v)-           else Nothing-  {-# INLINE decode #-}-  encode (PK v) = v-  {-# INLINE encode #-}+import Crypto.Saltine.Internal.Sign+            ( c_sign_keypair+            , c_sign+            , c_sign_open+            , c_sign_detached+            , c_sign_verify_detached+            , c_sign_ed25519_pk_to_curve25519+            , c_sign_ed25519_sk_to_curve25519+            , SecretKey(..)+            , PublicKey(..)+            , Keypair(..)+            , Signature(..)+            )+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Foreign.Marshal.Alloc+import Foreign.Storable+import System.IO.Unsafe --- | A convenience type for keypairs-type Keypair = (SecretKey, PublicKey)+import qualified Crypto.Saltine.Internal.Sign as Bytes+import qualified Crypto.Saltine.Internal.ScalarMult as SM+import qualified Data.ByteString              as S  -- | Creates a random key of the correct size for 'sign' and -- 'signOpen' of form @(secretKey, publicKey)@.@@ -77,10 +71,10 @@ newKeypair = do   -- This is a little bizarre and a likely source of errors.   -- _err ought to always be 0.-  ((_err, sk), pk) <- buildUnsafeByteString' Bytes.signPK $ \pkbuf ->-    buildUnsafeByteString' Bytes.signSK $ \skbuf ->+  ((_err, sk), pk) <- buildUnsafeByteString' Bytes.sign_publickeybytes $ \pkbuf ->+    buildUnsafeByteString' Bytes.sign_secretkeybytes $ \skbuf ->       c_sign_keypair pkbuf skbuf-  return (SK sk, PK pk)+  return $ Keypair (SK sk) (PK pk)  -- | Augments a message with a signature forming a \"signed -- message\".@@ -91,7 +85,7 @@      -- ^ Signed message sign (SK k) m = unsafePerformIO $   alloca $ \psmlen -> do-    (_err, sm) <- buildUnsafeByteString' (len + Bytes.sign) $ \psmbuf ->+    (_err, sm) <- buildUnsafeByteString' (len + Bytes.sign_bytes) $ \psmbuf ->       constByteStrings [k, m] $ \[(pk, _), (pm, _)] ->         c_sign psmbuf psmlen pm (fromIntegral len) pk     smlen <- peek psmlen@@ -121,87 +115,47 @@ signDetached :: SecretKey              -> ByteString              -- ^ Message-             -> ByteString+             -> Signature              -- ^ Signature signDetached (SK k) m = unsafePerformIO $     alloca $ \psmlen -> do-        (_err, sm) <- buildUnsafeByteString' Bytes.sign $ \sigbuf ->+        (_err, sm) <- buildUnsafeByteString' Bytes.sign_bytes $ \sigbuf ->             constByteStrings [k, m] $ \[(pk, _), (pm, _)] ->                 c_sign_detached sigbuf psmlen pm (fromIntegral len) pk         smlen <- peek psmlen-        return $ S.take (fromIntegral smlen) sm+        return $ Signature $ S.take (fromIntegral smlen) sm   where len = S.length m  -- | Returns @True@ if the signature is valid for the given public key and -- message. signVerifyDetached :: PublicKey-                   -> ByteString+                   -> Signature                    -- ^ Signature                    -> ByteString-                   -- ^ Message+                   -- ^ Message (not signed)                    -> Bool-signVerifyDetached (PK k) sig sm = unsafePerformIO $+signVerifyDetached (PK k) (Signature sig) sm = unsafePerformIO $     constByteStrings [k, sig, sm] $ \[(pk, _), (psig, _), (psm, _)] -> do         res <- c_sign_verify_detached psig psm (fromIntegral len) pk         return (res == 0)   where len = S.length sm --foreign import ccall "crypto_sign_keypair"-  c_sign_keypair :: Ptr CChar-                 -- ^ Public key output buffer-                 -> Ptr CChar-                 -- ^ Secret key output buffer-                 -> IO CInt-                 -- ^ Always 0--foreign import ccall "crypto_sign"-  c_sign :: Ptr CChar-         -- ^ Signed message output buffer-         -> Ptr CULLong-         -- ^ Length of signed message-         -> Ptr CChar-         -- ^ Constant message buffer-         -> CULLong-         -- ^ Length of message input buffer-         -> Ptr CChar-         -- ^ Constant secret key buffer-         -> IO CInt-         -- ^ Always 0--foreign import ccall "crypto_sign_open"-  c_sign_open :: Ptr CChar-              -- ^ Message output buffer-              -> Ptr CULLong-              -- ^ Length of message-              -> Ptr CChar-              -- ^ Constant signed message buffer-              -> CULLong-              -- ^ Length of signed message buffer-              -> Ptr CChar-              -- ^ Public key buffer-              -> IO CInt-              -- ^ 0 if signature is verifiable, -1 otherwise+-- | Converts an Ed25519 public key to an X25519 public key.+signPublicKeyToScalarMult :: PublicKey -> Maybe SM.GroupElement+signPublicKeyToScalarMult (PK pk) = unsafePerformIO $ do+  (err,x) <- buildUnsafeByteString' SM.scalarmult_bytes $ \xbuf ->+    constByteStrings [pk] $ \[(edbuf,_)] ->+      c_sign_ed25519_pk_to_curve25519 xbuf edbuf+  case err of+    0 -> return $ Just $ SM.GE x+    _ -> return   Nothing -foreign import ccall "crypto_sign_detached"-    c_sign_detached :: Ptr CChar-                    -- ^ Signature output buffer-                    -> Ptr CULLong-                    -- ^ Length of the signature-                    -> Ptr CChar-                    -- ^ Constant message buffer-                    -> CULLong-                    -- ^ Length of message buffer-                    -> Ptr CChar-                    -- ^ Constant secret key buffer-                    -> IO CInt-foreign import ccall "crypto_sign_verify_detached"-    c_sign_verify_detached :: Ptr CChar-                           -- ^ Signature buffer-                           -> Ptr CChar-                           -- ^ Constant signed message buffer-                           -> CULLong-                           -- ^ Length of signed message buffer-                           -> Ptr CChar-                           -- ^ Public key buffer-                           -> IO CInt+-- | Converts an Ed25519 secret key to an X25519 secret key.+signSecretKeyToScalarMult :: SecretKey -> Maybe SM.Scalar+signSecretKeyToScalarMult (SK sk) = unsafePerformIO $ do+  (err,x) <- buildUnsafeByteString' SM.scalarmult_bytes $ \xbuf ->+    constByteStrings [sk] $ \[(edbuf,_)] ->+      c_sign_ed25519_sk_to_curve25519 xbuf edbuf+  case err of+    0 -> return $ Just $ SM.Sc x+    _ -> return   Nothing
src/Crypto/Saltine/Core/Stream.hs view
@@ -53,52 +53,25 @@   stream, xor   ) where -import           Crypto.Saltine.Class-import           Crypto.Saltine.Internal.Util-import qualified Crypto.Saltine.Internal.ByteSizes as Bytes--import           Control.Applicative-import           Foreign.C-import           Foreign.Ptr-import qualified Data.ByteString as S-import           Data.ByteString (ByteString)---- $types---- | An opaque 'stream' cryptographic key.-newtype Key = Key ByteString deriving (Eq, Ord)--instance IsEncoding Key where-  decode v = if S.length v == Bytes.streamKey-           then Just (Key v)-           else Nothing-  {-# INLINE decode #-}-  encode (Key v) = v-  {-# INLINE encode #-}---- | An opaque 'stream' nonce.-newtype Nonce = Nonce ByteString deriving (Eq, Ord)--instance IsNonce Nonce where-  zero = Nonce (S.replicate Bytes.streamNonce 0)-  nudge (Nonce n) = Nonce (nudgeBS n)+import Crypto.Saltine.Internal.Stream ( c_stream+                                      , c_stream_xor+                                      , Key(..)+                                      , Nonce(..)+                                      )+import Crypto.Saltine.Internal.Util   as U+import Data.ByteString                (ByteString) -instance IsEncoding Nonce where-  decode v = if S.length v == Bytes.streamNonce-           then Just (Nonce v)-           else Nothing-  {-# INLINE decode #-}-  encode (Nonce v) = v-  {-# INLINE encode #-}+import qualified Crypto.Saltine.Internal.Stream as Bytes+import qualified Data.ByteString                as S  -- | Creates a random key of the correct size for 'stream' and 'xor'. newKey :: IO Key-newKey = Key <$> randomByteString Bytes.streamKey+newKey = Key <$> randomByteString Bytes.stream_keybytes  -- | Creates a random nonce of the correct size for 'stream' and -- 'xor'. newNonce :: IO Nonce-newNonce = Nonce <$> randomByteString Bytes.streamNonce+newNonce = Nonce <$> randomByteString Bytes.stream_noncebytes  -- | Generates a cryptographic random stream indexed by the 'Key' and -- 'Nonce'. These streams are indistinguishable from random noise so@@ -128,29 +101,3 @@     constByteStrings [key, nonce, msg] $ \[(pk, _), (pn, _), (pm, _)] ->     c_stream_xor pc pm (fromIntegral len) pn pk   where len = S.length msg--foreign import ccall "crypto_stream"-  c_stream :: Ptr CChar-           -- ^ Stream output buffer-           -> CULLong-           -- ^ Length of stream to generate-           -> Ptr CChar-           -- ^ Constant nonce buffer-           -> Ptr CChar-           -- ^ Constant key buffer-           -> IO CInt-           -- ^ Always 0--foreign import ccall "crypto_stream_xor"-  c_stream_xor :: Ptr CChar-               -- ^ Ciphertext output buffer-               -> Ptr CChar-               -- ^ Constant message buffer-               -> CULLong-               -- ^ Length of message buffer-               -> Ptr CChar-               -- ^ Constant nonce buffer-               -> Ptr CChar-               -- ^ Constant key buffer-               -> IO CInt-               -- ^ Always 0
src/Crypto/Saltine/Core/Utils.hs view
@@ -1,5 +1,6 @@ module Crypto.Saltine.Core.Utils-    (Crypto.Saltine.Internal.Util.randomByteString+    ( Crypto.Saltine.Internal.Util.randomByteString+    , Crypto.Saltine.Internal.Util.bin2hex     ) where  import qualified Crypto.Saltine.Internal.Util
+ src/Crypto/Saltine/Internal/AEAD/AES256GCM.hs view
@@ -0,0 +1,188 @@+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving, DeriveGeneric, ForeignFunctionInterface #-}+-- |+-- Module      : Crypto.Saltine.Internal.AEAD.AES256GCM+-- Copyright   : (c) Max Amanshauser 2021+-- License     : MIT+--+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable+--+module Crypto.Saltine.Internal.AEAD.AES256GCM (+    aead_aes256gcm_keybytes+  , aead_aes256gcm_npubbytes+  , aead_aes256gcm_abytes+  , c_aead_aes256gcm_is_available+  , c_aead+  , c_aead_open+  , c_aead_detached+  , c_aead_open_detached+  , Key(..)+  , Nonce(..)+) where++import Control.DeepSeq+import Crypto.Saltine.Class+import Crypto.Saltine.Core.Hash     (shorthash)+import Crypto.Saltine.Internal.Hash (nullShKey)+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Data.Data                    (Data, Typeable)+import Data.Hashable                (Hashable)+import Data.Monoid+import GHC.Generics                 (Generic)+import Foreign.C+import Foreign.Ptr++import qualified Data.ByteString as S++-- | An opaque 'AES256GCM' cryptographic key.+newtype Key = Key { unKey :: ByteString } deriving (Ord, Hashable, Data, Typeable, Generic, NFData)+instance Eq Key where+    Key a == Key b = U.compare a b+instance Show Key where+    show k = "AEAD.AES256GCM.Key {hashesTo = \"" <> (bin2hex . shorthash nullShKey $ encode k) <> "\"}"++instance IsEncoding Key where+  decode v = if S.length v == aead_aes256gcm_keybytes+           then Just (Key v)+           else Nothing+  {-# INLINE decode #-}+  encode (Key v) = v+  {-# INLINE encode #-}++-- | An opaque 'AES256GCM' nonce.+newtype Nonce = Nonce { unNonce :: ByteString } deriving (Eq, Ord, Hashable, Data, Typeable, Generic, NFData)+instance Show Nonce where+    show k = "AEAD.AES256GCM.Nonce " <> bin2hex (encode k)++instance IsEncoding Nonce where+  decode v = if S.length v == aead_aes256gcm_npubbytes+           then Just (Nonce v)+           else Nothing+  {-# INLINE decode #-}+  encode (Nonce v) = v+  {-# INLINE encode #-}++instance IsNonce Nonce where+  zero            = Nonce (S.replicate aead_aes256gcm_npubbytes 0)+  nudge (Nonce n) = Nonce (nudgeBS n)+++aead_aes256gcm_keybytes, aead_aes256gcm_abytes, aead_aes256gcm_npubbytes :: Int++-- | Size of an AES256 key+aead_aes256gcm_keybytes  = fromIntegral c_crypto_aead_aes256gcm_keybytes+-- | Size of an AES256 nonce+aead_aes256gcm_npubbytes = fromIntegral c_crypto_aead_aes256gcm_npubbytes+-- | Size of an AES256 authentication tag+aead_aes256gcm_abytes    = fromIntegral c_crypto_aead_aes256gcm_abytes+++-- src/libsodium/crypto_aead/aes256gcm/sodium/aead_aes256gcm.c+-- src/libsodium/include/sodium/crypto_aead_aes256gcm.h+foreign import ccall "crypto_aead_aes256gcm_keybytes"+    c_crypto_aead_aes256gcm_keybytes :: CSize+foreign import ccall "crypto_aead_aes256gcm_npubbytes"+    c_crypto_aead_aes256gcm_npubbytes:: CSize+foreign import ccall "crypto_aead_aes256gcm_abytes"+    c_crypto_aead_aes256gcm_abytes :: CSize+++foreign import ccall "crypto_aead_aes256gcm_is_available"+  c_aead_aes256gcm_is_available+    :: IO CInt++-- | The aead C API uses C strings. Always returns 0.+foreign import ccall "crypto_aead_aes256gcm_encrypt"+  c_aead+    :: Ptr CChar+    -- ^ Cipher output buffer+    -> Ptr CULLong+    -- ^ Cipher output bytes used+    -> Ptr CChar+    -- ^ Constant message input buffer+    -> CULLong+    -- ^ Length of message input buffer+    -> Ptr CChar+    -- ^ Constant aad input buffer+    -> CULLong+    -- ^ Length of aad input buffer+    -> Ptr CChar+    -- ^ Unused 'nsec' value (must be NULL)+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt++-- | The aead open C API uses C strings. Returns 0 if successful.+foreign import ccall "crypto_aead_aes256gcm_decrypt"+  c_aead_open+    :: Ptr CChar+    -- ^ Message output buffer+    -> Ptr CULLong+    -- ^ Message output bytes used+    -> Ptr CChar+    -- ^ Unused 'nsec' value (must be NULL)+    -> Ptr CChar+    -- ^ Constant ciphertext input buffer+    -> CULLong+    -- ^ Length of ciphertext input buffer+    -> Ptr CChar+    -- ^ Constant aad input buffer+    -> CULLong+    -- ^ Length of aad input buffer+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt++-- | The aead C API uses C strings. Always returns 0.+foreign import ccall "crypto_aead_aes256gcm_encrypt_detached"+  c_aead_detached+    :: Ptr CChar+    -- ^ Cipher output buffer+    -> Ptr CChar+    -- ^ Tag output buffer+    -> Ptr CULLong+    -- ^ Tag bytes used+    -> Ptr CChar+    -- ^ Constant message input buffer+    -> CULLong+    -- ^ Length of message input buffer+    -> Ptr CChar+    -- ^ Constant aad input buffer+    -> CULLong+    -- ^ Length of aad input buffer+    -> Ptr CChar+    -- ^ Unused 'nsec' value (must be NULL)+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt++-- | The aead open C API uses C strings. Returns 0 if successful.+foreign import ccall "crypto_aead_aes256gcm_decrypt_detached"+  c_aead_open_detached+    :: Ptr CChar+    -- ^ Message output buffer+    -> Ptr CChar+    -- ^ Unused 'nsec' value (must be NULL)+    -> Ptr CChar+    -- ^ Constant ciphertext input buffer+    -> CULLong+    -- ^ Length of ciphertext input buffer+    -> Ptr CChar+    -- ^ Constant tag input buffer+    -> Ptr CChar+    -- ^ Constant aad input buffer+    -> CULLong+    -- ^ Length of aad input buffer+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt
+ src/Crypto/Saltine/Internal/AEAD/ChaCha20Poly1305.hs view
@@ -0,0 +1,184 @@+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving, DeriveGeneric, ForeignFunctionInterface #-}+-- |+-- Module      : Crypto.Saltine.Internal.AEAD.ChaCha20Poly1305+-- Copyright   : (c) Max Amanshauser 2021+-- License     : MIT+--+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable+--+module Crypto.Saltine.Internal.AEAD.ChaCha20Poly1305 (+    aead_chacha20poly1305_keybytes+  , aead_chacha20poly1305_npubbytes+  , aead_chacha20poly1305_abytes+  , c_aead+  , c_aead_open+  , c_aead_detached+  , c_aead_open_detached+  , Key(..)+  , Nonce(..)+) where++import Control.DeepSeq+import Crypto.Saltine.Class+import Crypto.Saltine.Core.Hash     (shorthash)+import Crypto.Saltine.Internal.Hash (nullShKey)+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Data.Data                    (Data, Typeable)+import Data.Hashable                (Hashable)+import Data.Monoid+import Foreign.C+import Foreign.Ptr+import GHC.Generics                 (Generic)++import qualified Data.ByteString as S+++-- | An opaque 'ChaCha20Poly1305' cryptographic key.+newtype Key = Key { unKey :: ByteString } deriving (Ord, Hashable, Data, Typeable, Generic, NFData)+instance Eq Key where+    Key a == Key b = U.compare a b+instance Show Key where+    show k = "AEAD.ChaCha20Poly1305.Key {hashesTo = \"" <> (bin2hex . shorthash nullShKey $ encode k) <> "\"}"++instance IsEncoding Key where+  decode v = if S.length v == aead_chacha20poly1305_keybytes+           then Just (Key v)+           else Nothing+  {-# INLINE decode #-}+  encode (Key v) = v+  {-# INLINE encode #-}++-- | An opaque 'ChaCha20Poly1305' nonce.+newtype Nonce = Nonce { unNonce :: ByteString } deriving (Eq, Ord, Hashable, Data, Typeable, Generic, NFData)+instance Show Nonce where+    show k = "AEAD.ChaCha20Poly1305.Nonce " <> bin2hex (encode k)++instance IsEncoding Nonce where+  decode v = if S.length v == aead_chacha20poly1305_npubbytes+           then Just (Nonce v)+           else Nothing+  {-# INLINE decode #-}+  encode (Nonce v) = v+  {-# INLINE encode #-}++instance IsNonce Nonce where+  zero            = Nonce (S.replicate aead_chacha20poly1305_npubbytes 0)+  nudge (Nonce n) = Nonce (nudgeBS n)+++aead_chacha20poly1305_keybytes, aead_chacha20poly1305_abytes, aead_chacha20poly1305_npubbytes :: Int++-- | Size of a ChaCha20-Poly1305 key+aead_chacha20poly1305_keybytes  = fromIntegral c_crypto_aead_chacha20poly1305_keybytes+-- | Size of a ChaCha20-Poly1305 nonce+aead_chacha20poly1305_npubbytes = fromIntegral c_crypto_aead_chacha20poly1305_npubbytes+-- | Size of a ChaCha20-Poly1305 authentication tag+aead_chacha20poly1305_abytes    = fromIntegral c_crypto_aead_chacha20poly1305_abytes+++-- src/libsodium/crypto_aead/xchacha20poly1305/sodium/aead_xchacha20poly1305.c+-- src/libsodium/include/sodium/crypto_aead_xchacha20poly1305.h+foreign import ccall "crypto_aead_chacha20poly1305_keybytes"+    c_crypto_aead_chacha20poly1305_keybytes :: CSize+foreign import ccall "crypto_aead_chacha20poly1305_npubbytes"+    c_crypto_aead_chacha20poly1305_npubbytes:: CSize+foreign import ccall "crypto_aead_chacha20poly1305_abytes"+    c_crypto_aead_chacha20poly1305_abytes :: CSize+++-- | The aead C API uses C strings. Always returns 0.+foreign import ccall "crypto_aead_chacha20poly1305_encrypt"+  c_aead+    :: Ptr CChar+    -- ^ Cipher output buffer+    -> Ptr CULLong+    -- ^ Cipher output bytes used+    -> Ptr CChar+    -- ^ Constant message input buffer+    -> CULLong+    -- ^ Length of message input buffer+    -> Ptr CChar+    -- ^ Constant aad input buffer+    -> CULLong+    -- ^ Length of aad input buffer+    -> Ptr CChar+    -- ^ Unused 'nsec' value (must be NULL)+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt++-- | The aead open C API uses C strings. Returns 0 if successful.+foreign import ccall "crypto_aead_chacha20poly1305_decrypt"+  c_aead_open+    :: Ptr CChar+    -- ^ Message output buffer+    -> Ptr CULLong+    -- ^ Message output bytes used+    -> Ptr CChar+    -- ^ Unused 'nsec' value (must be NULL)+    -> Ptr CChar+    -- ^ Constant ciphertext input buffer+    -> CULLong+    -- ^ Length of ciphertext input buffer+    -> Ptr CChar+    -- ^ Constant aad input buffer+    -> CULLong+    -- ^ Length of aad input buffer+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt++-- | The aead C API uses C strings. Always returns 0.+foreign import ccall "crypto_aead_chacha20poly1305_encrypt_detached"+  c_aead_detached+    :: Ptr CChar+    -- ^ Cipher output buffer+    -> Ptr CChar+    -- ^ Tag output buffer+    -> Ptr CULLong+    -- ^ Tag bytes used+    -> Ptr CChar+    -- ^ Constant message input buffer+    -> CULLong+    -- ^ Length of message input buffer+    -> Ptr CChar+    -- ^ Constant aad input buffer+    -> CULLong+    -- ^ Length of aad input buffer+    -> Ptr CChar+    -- ^ Unused 'nsec' value (must be NULL)+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt++-- | The aead open C API uses C strings. Returns 0 if successful.+foreign import ccall "crypto_aead_chacha20poly1305_decrypt_detached"+  c_aead_open_detached+    :: Ptr CChar+    -- ^ Message output buffer+    -> Ptr CChar+    -- ^ Unused 'nsec' value (must be NULL)+    -> Ptr CChar+    -- ^ Constant ciphertext input buffer+    -> CULLong+    -- ^ Length of ciphertext input buffer+    -> Ptr CChar+    -- ^ Constant tag input buffer+    -> Ptr CChar+    -- ^ Constant aad input buffer+    -> CULLong+    -- ^ Length of aad input buffer+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt
+ src/Crypto/Saltine/Internal/AEAD/ChaCha20Poly1305IETF.hs view
@@ -0,0 +1,183 @@+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving, DeriveGeneric, ForeignFunctionInterface #-}+-- |+-- Module      : Crypto.Saltine.Internal.AEAD.ChaCha20Poly1305IETF+-- Copyright   : (c) Max Amanshauser 2021+-- License     : MIT+--+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable+--+module Crypto.Saltine.Internal.AEAD.ChaCha20Poly1305IETF (+    aead_chacha20poly1305_ietf_keybytes+  , aead_chacha20poly1305_ietf_npubbytes+  , aead_chacha20poly1305_ietf_abytes+  , c_aead+  , c_aead_open+  , c_aead_detached+  , c_aead_open_detached+  , Key(..)+  , Nonce(..)+) where++import Control.DeepSeq+import Crypto.Saltine.Class+import Crypto.Saltine.Core.Hash     (shorthash)+import Crypto.Saltine.Internal.Hash (nullShKey)+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Data.Data                    (Data, Typeable)+import Data.Hashable                (Hashable)+import Data.Monoid+import Foreign.C+import Foreign.Ptr+import GHC.Generics                 (Generic)++import qualified Data.ByteString as S+++-- | An opaque 'ChaCha20Poly1305IETF' cryptographic key.+newtype Key = Key { unKey :: ByteString } deriving (Ord, Hashable, Data, Typeable, Generic, NFData)+instance Eq Key where+    Key a == Key b = U.compare a b+instance Show Key where+    show k = "AEAD.ChaCha20Poly1305IETF.Key {hashesTo = \"" <> (bin2hex . shorthash nullShKey $ encode k) <> "\"}"++instance IsEncoding Key where+  decode v = if S.length v == aead_chacha20poly1305_ietf_keybytes+           then Just (Key v)+           else Nothing+  {-# INLINE decode #-}+  encode (Key v) = v+  {-# INLINE encode #-}++-- | An opaque 'ChaCha20Poly1305IETF' nonce.+newtype Nonce = Nonce { unNonce :: ByteString } deriving (Eq, Ord, Hashable, Data, Typeable, Generic, NFData)+instance Show Nonce where+    show k = "AEAD.ChaCha20Poly1305IETF.Nonce " <> bin2hex (encode k)++instance IsEncoding Nonce where+  decode v = if S.length v == aead_chacha20poly1305_ietf_npubbytes+           then Just (Nonce v)+           else Nothing+  {-# INLINE decode #-}+  encode (Nonce v) = v+  {-# INLINE encode #-}++instance IsNonce Nonce where+  zero            = Nonce (S.replicate aead_chacha20poly1305_ietf_npubbytes 0)+  nudge (Nonce n) = Nonce (nudgeBS n)++aead_chacha20poly1305_ietf_keybytes, aead_chacha20poly1305_ietf_abytes, aead_chacha20poly1305_ietf_npubbytes :: Int++-- | Size of a ChaCha20-Poly1305-IETF key+aead_chacha20poly1305_ietf_keybytes  = fromIntegral c_crypto_aead_chacha20poly1305_ietf_keybytes+-- | Size of a ChaCha20-Poly1305-IETF nonce+aead_chacha20poly1305_ietf_npubbytes = fromIntegral c_crypto_aead_chacha20poly1305_ietf_npubbytes+-- | Size of a ChaCha20-Poly1305-IETF authentication tag+aead_chacha20poly1305_ietf_abytes    = fromIntegral c_crypto_aead_chacha20poly1305_ietf_abytes+++-- src/libsodium/crypto_aead/chacha20poly1305/sodium/aead_chacha20poly1305.c+-- src/libsodium/include/sodium/crypto_aead_chacha20poly1305.h+foreign import ccall "crypto_aead_chacha20poly1305_ietf_keybytes"+    c_crypto_aead_chacha20poly1305_ietf_keybytes :: CSize+foreign import ccall "crypto_aead_chacha20poly1305_ietf_npubbytes"+    c_crypto_aead_chacha20poly1305_ietf_npubbytes:: CSize+foreign import ccall "crypto_aead_chacha20poly1305_ietf_abytes"+    c_crypto_aead_chacha20poly1305_ietf_abytes :: CSize+++-- | The aead C API uses C strings. Always returns 0.+foreign import ccall "crypto_aead_chacha20poly1305_ietf_encrypt"+  c_aead+    :: Ptr CChar+    -- ^ Cipher output buffer+    -> Ptr CULLong+    -- ^ Cipher output bytes used+    -> Ptr CChar+    -- ^ Constant message input buffer+    -> CULLong+    -- ^ Length of message input buffer+    -> Ptr CChar+    -- ^ Constant aad input buffer+    -> CULLong+    -- ^ Length of aad input buffer+    -> Ptr CChar+    -- ^ Unused 'nsec' value (must be NULL)+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt++-- | The aead open C API uses C strings. Returns 0 if successful.+foreign import ccall "crypto_aead_chacha20poly1305_ietf_decrypt"+  c_aead_open+    :: Ptr CChar+    -- ^ Message output buffer+    -> Ptr CULLong+    -- ^ Message output bytes used+    -> Ptr CChar+    -- ^ Unused 'nsec' value (must be NULL)+    -> Ptr CChar+    -- ^ Constant ciphertext input buffer+    -> CULLong+    -- ^ Length of ciphertext input buffer+    -> Ptr CChar+    -- ^ Constant aad input buffer+    -> CULLong+    -- ^ Length of aad input buffer+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt++-- | The aead C API uses C strings. Always returns 0.+foreign import ccall "crypto_aead_chacha20poly1305_ietf_encrypt_detached"+  c_aead_detached+    :: Ptr CChar+    -- ^ Cipher output buffer+    -> Ptr CChar+    -- ^ Tag output buffer+    -> Ptr CULLong+    -- ^ Tag bytes used+    -> Ptr CChar+    -- ^ Constant message input buffer+    -> CULLong+    -- ^ Length of message input buffer+    -> Ptr CChar+    -- ^ Constant aad input buffer+    -> CULLong+    -- ^ Length of aad input buffer+    -> Ptr CChar+    -- ^ Unused 'nsec' value (must be NULL)+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt++-- | The aead open C API uses C strings. Returns 0 if successful.+foreign import ccall "crypto_aead_chacha20poly1305_ietf_decrypt_detached"+  c_aead_open_detached+    :: Ptr CChar+    -- ^ Message output buffer+    -> Ptr CChar+    -- ^ Unused 'nsec' value (must be NULL)+    -> Ptr CChar+    -- ^ Constant ciphertext input buffer+    -> CULLong+    -- ^ Length of ciphertext input buffer+    -> Ptr CChar+    -- ^ Constant tag input buffer+    -> Ptr CChar+    -- ^ Constant aad input buffer+    -> CULLong+    -- ^ Length of aad input buffer+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt
+ src/Crypto/Saltine/Internal/AEAD/XChaCha20Poly1305.hs view
@@ -0,0 +1,183 @@+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving, DeriveGeneric, ForeignFunctionInterface #-}+-- |+-- Module      : Crypto.Saltine.Internal.AEAD.XChaCha20Poly1305+-- Copyright   : (c) Max Amanshauser 2021+-- License     : MIT+--+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable+--+module Crypto.Saltine.Internal.AEAD.XChaCha20Poly1305 (+    aead_xchacha20poly1305_ietf_keybytes+  , aead_xchacha20poly1305_ietf_npubbytes+  , aead_xchacha20poly1305_ietf_abytes+  , c_aead+  , c_aead_open+  , c_aead_detached+  , c_aead_open_detached+  , Key(..)+  , Nonce(..)+) where++import Control.DeepSeq+import Crypto.Saltine.Class+import Crypto.Saltine.Core.Hash     (shorthash)+import Crypto.Saltine.Internal.Hash (nullShKey)+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Data.Data                    (Data, Typeable)+import Data.Hashable                (Hashable)+import Data.Monoid+import Foreign.C+import Foreign.Ptr+import GHC.Generics                 (Generic)++import qualified Data.ByteString as S++-- | An opaque 'XChaCha20Poly1305' cryptographic key.+newtype Key = Key { unKey :: ByteString } deriving (Ord, Hashable, Data, Typeable, Generic, NFData)+instance Eq Key where+    Key a == Key b = U.compare a b+instance Show Key where+    show k = "AEAD.XChaCha20Poly1305.Key {hashesTo = \"" <> (bin2hex . shorthash nullShKey $ encode k) <> "\"}"++instance IsEncoding Key where+  decode v = if S.length v == aead_xchacha20poly1305_ietf_keybytes+           then Just (Key v)+           else Nothing+  {-# INLINE decode #-}+  encode (Key v) = v+  {-# INLINE encode #-}++-- | An opaque 'XChaCha20Poly1305' nonce.+newtype Nonce = Nonce { unNonce :: ByteString } deriving (Eq, Ord, Hashable, Data, Typeable, Generic, NFData)+instance Show Nonce where+    show k = "AEAD.XChaCha20Poly1305.Nonce " <> bin2hex (encode k)++instance IsEncoding Nonce where+  decode v = if S.length v == aead_xchacha20poly1305_ietf_npubbytes+           then Just (Nonce v)+           else Nothing+  {-# INLINE decode #-}+  encode (Nonce v) = v+  {-# INLINE encode #-}++instance IsNonce Nonce where+  zero            = Nonce (S.replicate aead_xchacha20poly1305_ietf_npubbytes 0)+  nudge (Nonce n) = Nonce (nudgeBS n)+++aead_xchacha20poly1305_ietf_keybytes, aead_xchacha20poly1305_ietf_abytes, aead_xchacha20poly1305_ietf_npubbytes :: Int++-- | Size of a XChaCha20-Poly1305 key+aead_xchacha20poly1305_ietf_keybytes  = fromIntegral c_crypto_aead_xchacha20poly1305_ietf_keybytes+-- | Size of a XChaCha20-Poly1305 nonce+aead_xchacha20poly1305_ietf_npubbytes = fromIntegral c_crypto_aead_xchacha20poly1305_ietf_npubbytes+-- | Size of a XChaCha20-Poly1305 authentication tag+aead_xchacha20poly1305_ietf_abytes    = fromIntegral c_crypto_aead_xchacha20poly1305_ietf_abytes+++-- src/libsodium/crypto_aead/xchacha20poly1305/sodium/aead_xchacha20poly1305.c+-- src/libsodium/include/sodium/crypto_aead_xchacha20poly1305.h+foreign import ccall "crypto_aead_xchacha20poly1305_ietf_keybytes"+    c_crypto_aead_xchacha20poly1305_ietf_keybytes :: CSize+foreign import ccall "crypto_aead_xchacha20poly1305_ietf_npubbytes"+    c_crypto_aead_xchacha20poly1305_ietf_npubbytes:: CSize+foreign import ccall "crypto_aead_xchacha20poly1305_ietf_abytes"+    c_crypto_aead_xchacha20poly1305_ietf_abytes :: CSize+++-- | The aead C API uses C strings. Always returns 0.+foreign import ccall "crypto_aead_xchacha20poly1305_ietf_encrypt"+  c_aead+    :: Ptr CChar+    -- ^ Cipher output buffer+    -> Ptr CULLong+    -- ^ Cipher output bytes used+    -> Ptr CChar+    -- ^ Constant message input buffer+    -> CULLong+    -- ^ Length of message input buffer+    -> Ptr CChar+    -- ^ Constant aad input buffer+    -> CULLong+    -- ^ Length of aad input buffer+    -> Ptr CChar+    -- ^ Unused 'nsec' value (must be NULL)+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt++-- | The aead open C API uses C strings. Returns 0 if successful.+foreign import ccall "crypto_aead_xchacha20poly1305_ietf_decrypt"+  c_aead_open+    :: Ptr CChar+    -- ^ Message output buffer+    -> Ptr CULLong+    -- ^ Message output bytes used+    -> Ptr CChar+    -- ^ Unused 'nsec' value (must be NULL)+    -> Ptr CChar+    -- ^ Constant ciphertext input buffer+    -> CULLong+    -- ^ Length of ciphertext input buffer+    -> Ptr CChar+    -- ^ Constant aad input buffer+    -> CULLong+    -- ^ Length of aad input buffer+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt++-- | The aead C API uses C strings. Always returns 0.+foreign import ccall "crypto_aead_xchacha20poly1305_ietf_encrypt_detached"+  c_aead_detached+    :: Ptr CChar+    -- ^ Cipher output buffer+    -> Ptr CChar+    -- ^ Tag output buffer+    -> Ptr CULLong+    -- ^ Tag bytes used+    -> Ptr CChar+    -- ^ Constant message input buffer+    -> CULLong+    -- ^ Length of message input buffer+    -> Ptr CChar+    -- ^ Constant aad input buffer+    -> CULLong+    -- ^ Length of aad input buffer+    -> Ptr CChar+    -- ^ Unused 'nsec' value (must be NULL)+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt++-- | The aead open C API uses C strings. Returns 0 if successful.+foreign import ccall "crypto_aead_xchacha20poly1305_ietf_decrypt_detached"+  c_aead_open_detached+    :: Ptr CChar+    -- ^ Message output buffer+    -> Ptr CChar+    -- ^ Unused 'nsec' value (must be NULL)+    -> Ptr CChar+    -- ^ Constant ciphertext input buffer+    -> CULLong+    -- ^ Length of ciphertext input buffer+    -> Ptr CChar+    -- ^ Constant tag input buffer+    -> Ptr CChar+    -- ^ Constant aad input buffer+    -> CULLong+    -- ^ Length of aad input buffer+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt
+ src/Crypto/Saltine/Internal/Auth.hs view
@@ -0,0 +1,103 @@+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving, DeriveGeneric, ForeignFunctionInterface #-}+-- |+-- Module      : Crypto.Saltine.Internal.Auth+-- Copyright   : (c) Max Amanshauser 2021+-- License     : MIT+--+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable+--+module Crypto.Saltine.Internal.Auth (+    auth_bytes+  , auth_keybytes+  , c_auth+  , c_auth_verify+  , Key(..)+  , Authenticator(..)+) where+++import Control.DeepSeq+import Crypto.Saltine.Class+import Crypto.Saltine.Core.Hash     (shorthash)+import Crypto.Saltine.Internal.Hash (nullShKey)+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Data.Data                    (Data, Typeable)+import Data.Hashable                (Hashable)+import Data.Monoid+import GHC.Generics                 (Generic)+import Foreign.C+import Foreign.Ptr++import qualified Data.ByteString as S++-- | An opaque 'auth' cryptographic key.+newtype Key = Key { unKey :: ByteString } deriving (Ord, Hashable, Data, Typeable, Generic, NFData)+instance Eq Key where+    Key a == Key b = U.compare a b+instance Show Key where+    show k = "Auth.Key {hashesTo = \"" <> (bin2hex . shorthash nullShKey $ encode k) <> "\"}"++instance IsEncoding Key where+  decode v = if S.length v == auth_keybytes+           then Just (Key v)+           else Nothing+  {-# INLINE decode #-}+  encode (Key v) = v+  {-# INLINE encode #-}++-- | An opaque 'auth' authenticator.+newtype Authenticator = Au { unAu :: ByteString } deriving (Eq, Ord, Hashable, Data, Typeable, Generic, NFData)+instance Show Authenticator where+    show k = "Auth.Authenticator " <> bin2hex (encode k)++instance IsEncoding Authenticator where+  decode v = if S.length v == auth_bytes+           then Just (Au v)+           else Nothing+  {-# INLINE decode #-}+  encode (Au v) = v+  {-# INLINE encode #-}+++auth_bytes, auth_keybytes :: Int++-- Authentication+-- | Size of a @crypto_auth@ authenticator.+auth_bytes    = fromIntegral c_crypto_auth_bytes+-- | Size of a @crypto_auth@ authenticator key.+auth_keybytes = fromIntegral c_crypto_auth_keybytes++-- src/libsodium/crypto_auth/crypto_auth.c+foreign import ccall "crypto_auth_bytes"+  c_crypto_auth_bytes :: CSize+foreign import ccall "crypto_auth_keybytes"+  c_crypto_auth_keybytes :: CSize++foreign import ccall "crypto_auth"+  c_auth :: Ptr CChar+         -- ^ Authenticator output buffer+         -> Ptr CChar+         -- ^ Constant message buffer+         -> CULLong+         -- ^ Length of message buffer+         -> Ptr CChar+         -- ^ Constant key buffer+         -> IO CInt+         -- ^ Always 0++-- | We don't even include this in the IO monad since all of the+-- buffers are constant.+foreign import ccall "crypto_auth_verify"+  c_auth_verify :: Ptr CChar+                -- ^ Constant authenticator buffer+                -> Ptr CChar+                -- ^ Constant message buffer+                -> CULLong+                -- ^ Length of message buffer+                -> Ptr CChar+                -- ^ Constant key buffer+                -> CInt+                -- ^ Success if 0, failure if -1
+ src/Crypto/Saltine/Internal/Box.hs view
@@ -0,0 +1,285 @@+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving, DeriveGeneric, ForeignFunctionInterface #-}+-- |+-- Module      : Crypto.Saltine.Internal.Box+-- Copyright   : (c) Max Amanshauser 2021+-- License     : MIT+--+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable+--+module Crypto.Saltine.Internal.Box (+    box_publickeybytes+  , box_secretkeybytes+  , box_noncebytes+  , box_zerobytes+  , box_boxzerobytes+  , box_macbytes+  , box_beforenmbytes+  , box_sealbytes+  , c_box_keypair+  , c_box_easy+  , c_box_open_easy+  , c_box_beforenm+  , c_box_easy_afternm+  , c_box_open_easy_afternm+  , c_box_seal+  , c_box_seal_open+  , SecretKey(..)+  , PublicKey(..)+  , Keypair(..)+  , CombinedKey(..)+  , Nonce(..)+) where++import Control.DeepSeq              (NFData)+import Crypto.Saltine.Class+import Crypto.Saltine.Core.Hash     (shorthash)+import Crypto.Saltine.Internal.Hash (nullShKey)+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Data.Data                    (Data, Typeable)+import Data.Hashable                (Hashable)+import Data.Monoid+import Foreign.C+import Foreign.Ptr+import GHC.Generics                 (Generic)++import qualified Data.ByteString as S++-- | An opaque 'box' cryptographic secret key.+newtype SecretKey = SK { unSK :: ByteString } deriving (Ord, Hashable, Data, Typeable, Generic, NFData)+instance Eq SecretKey where+    SK a == SK b = U.compare a b+instance Show SecretKey where+    show k = "Box.SecretKey {hashesTo = \"" <> (bin2hex . shorthash nullShKey $ encode k) <> "\"}"++instance IsEncoding SecretKey where+  decode v = if S.length v == box_secretkeybytes+           then Just (SK v)+           else Nothing+  {-# INLINE decode #-}+  encode (SK v) = v+  {-# INLINE encode #-}++-- | An opaque 'box' cryptographic public key.+newtype PublicKey = PK { unPK :: ByteString } deriving (Ord, Hashable, Data, Typeable, Generic, NFData)+instance Eq PublicKey where+    PK a == PK b = U.compare a b+instance Show PublicKey where+    show k = "Box.PublicKey {hashesTo = \"" <> (bin2hex . shorthash nullShKey $ encode k) <> "\"}"++instance IsEncoding PublicKey where+  decode v = if S.length v == box_publickeybytes+           then Just (PK v)+           else Nothing+  {-# INLINE decode #-}+  encode (PK v) = v+  {-# INLINE encode #-}++-- | A convenience type for keypairs+data Keypair = Keypair {+    secretKey :: SecretKey+  , publicKey :: PublicKey+} deriving (Ord, Data, Typeable, Generic)++instance Eq Keypair where+    kp1 == kp2 = U.compare (encode $ secretKey kp1) (encode $ secretKey kp2)+            !&&! U.compare (encode $ publicKey kp1) (encode $ publicKey kp2)++instance Show Keypair where+    show k = "Box.Keypair {secretKey = " <> show (secretKey k) <> ", publicKey = " <> show (publicKey k) <> "}"++instance Hashable Keypair+instance NFData   Keypair++-- | An opaque 'boxAfterNM' cryptographic combined key.+newtype CombinedKey = CK { unCK :: ByteString } deriving (Ord, Hashable, Data, Typeable, Generic, NFData)+instance Eq CombinedKey where+    CK a == CK b = U.compare a b+instance Show CombinedKey where+    show k = "Box.CombinedKey {hashesTo = \"" <> (bin2hex . shorthash nullShKey $ encode k) <> "\"}"++instance IsEncoding CombinedKey where+  decode v = if S.length v == box_beforenmbytes+           then Just (CK v)+           else Nothing+  {-# INLINE decode #-}+  encode (CK v) = v+  {-# INLINE encode #-}++-- | An opaque 'box' nonce.+newtype Nonce = Nonce { unNonce :: ByteString } deriving (Eq, Ord, Hashable, Data, Typeable, Generic, NFData)+instance Show Nonce where+    show k = "Box.Nonce " <> bin2hex (encode k)++instance IsEncoding Nonce where+  decode v = if S.length v == box_noncebytes+           then Just (Nonce v)+           else Nothing+  {-# INLINE decode #-}+  encode (Nonce v) = v+  {-# INLINE encode #-}++instance IsNonce Nonce where+  zero            = Nonce (S.replicate box_noncebytes 0)+  nudge (Nonce n) = Nonce (nudgeBS n)+++box_publickeybytes, box_secretkeybytes, box_noncebytes, box_zerobytes, box_boxzerobytes :: Int+box_macbytes, box_beforenmbytes, box_sealbytes :: Int++-- Box+-- | Size of a @crypto_box@ public key+box_publickeybytes  = fromIntegral c_crypto_box_publickeybytes+-- | Size of a @crypto_box@ secret key+box_secretkeybytes  = fromIntegral c_crypto_box_secretkeybytes+-- | Size of a @crypto_box@ nonce+box_noncebytes      = fromIntegral c_crypto_box_noncebytes+-- | Size of 0-padding prepended to messages before using @crypto_box@+-- or after using @crypto_box_open@+box_zerobytes       = fromIntegral c_crypto_box_zerobytes+-- | Size of 0-padding prepended to ciphertext before using+-- @crypto_box_open@ or after using @crypto_box@.+box_boxzerobytes    = fromIntegral c_crypto_box_boxzerobytes+box_macbytes        = fromIntegral c_crypto_box_macbytes+-- | Size of a @crypto_box_beforenm@-generated combined key+box_beforenmbytes   = fromIntegral c_crypto_box_beforenmbytes++-- SealedBox+-- | Amount by which ciphertext is longer than plaintext+-- in sealed boxes+box_sealbytes       = fromIntegral c_crypto_box_sealbytes++-- src/libsodium/crypto_box/crypto_box.c+foreign import ccall "crypto_box_publickeybytes"+  c_crypto_box_publickeybytes :: CSize+foreign import ccall "crypto_box_secretkeybytes"+  c_crypto_box_secretkeybytes :: CSize+foreign import ccall "crypto_box_beforenmbytes"+  c_crypto_box_beforenmbytes :: CSize+foreign import ccall "crypto_box_noncebytes"+  c_crypto_box_noncebytes :: CSize+foreign import ccall "crypto_box_zerobytes"+  c_crypto_box_zerobytes :: CSize+foreign import ccall "crypto_box_boxzerobytes"+  c_crypto_box_boxzerobytes :: CSize+foreign import ccall "crypto_box_macbytes"+  c_crypto_box_macbytes :: CSize++-- src/libsodium/crypto_box_seal.c+foreign import ccall "crypto_box_sealbytes"+  c_crypto_box_sealbytes :: CSize++-- | Should always return a 0.+foreign import ccall "crypto_box_keypair"+  c_box_keypair :: Ptr CChar+                -- ^ Public key+                -> Ptr CChar+                -- ^ Secret key+                -> IO CInt+                -- ^ Always 0++-- | The secretbox C API uses C strings.+foreign import ccall "crypto_box_easy"+  c_box_easy :: Ptr CChar+             -- ^ Cipher output buffer+             -> Ptr CChar+             -- ^ Constant message input buffer+             -> CULLong+             -- ^ Length of message input buffer+             -> Ptr CChar+             -- ^ Constant nonce buffer+             -> Ptr CChar+             -- ^ Constant public key buffer+             -> Ptr CChar+             -- ^ Constant secret key buffer+             -> IO CInt+             -- ^ Always 0++-- | The secretbox C API uses C strings.+foreign import ccall "crypto_box_open_easy"+  c_box_open_easy :: Ptr CChar+                  -- ^ Message output buffer+                  -> Ptr CChar+                  -- ^ Constant ciphertext input buffer+                  -> CULLong+                  -- ^ Length of message input buffer+                  -> Ptr CChar+                  -- ^ Constant nonce buffer+                  -> Ptr CChar+                  -- ^ Constant public key buffer+                  -> Ptr CChar+                  -- ^ Constant secret key buffer+                  -> IO CInt+                  -- ^ 0 for success, -1 for failure to verify++-- | Single target key precompilation.+foreign import ccall "crypto_box_beforenm"+  c_box_beforenm :: Ptr CChar+                 -- ^ Combined key output buffer+                 -> Ptr CChar+                 -- ^ Constant public key buffer+                 -> Ptr CChar+                 -- ^ Constant secret key buffer+                 -> IO CInt+                 -- ^ Always 0++-- | Precompiled key crypto box. Uses C strings.+foreign import ccall "crypto_box_easy_afternm"+  c_box_easy_afternm :: Ptr CChar+                     -- ^ Cipher output buffer+                     -> Ptr CChar+                     -- ^ Constant message input buffer+                     -> CULLong+                     -- ^ Length of message input buffer (incl. 0s)+                     -> Ptr CChar+                     -- ^ Constant nonce buffer+                     -> Ptr CChar+                     -- ^ Constant combined key buffer+                     -> IO CInt+                     -- ^ Always 0++-- | The secretbox C API uses C strings.+foreign import ccall "crypto_box_open_easy_afternm"+  c_box_open_easy_afternm :: Ptr CChar+                          -- ^ Message output buffer+                          -> Ptr CChar+                          -- ^ Constant ciphertext input buffer+                          -> CULLong+                          -- ^ Length of message input buffer (incl. 0s)+                          -> Ptr CChar+                          -- ^ Constant nonce buffer+                          -> Ptr CChar+                          -- ^ Constant combined key buffer+                          -> IO CInt+                          -- ^ 0 for success, -1 for failure to verify+++-- | The sealedbox C API uses C strings.+foreign import ccall "crypto_box_seal"+  c_box_seal :: Ptr CChar+             -- ^ Cipher output buffer+             -> Ptr CChar+             -- ^ Constant message input buffer+             -> CULLong+             -- ^ Length of message input buffer+             -> Ptr CChar+             -- ^ Constant public key buffer+             -> IO CInt+             -- ^ Always 0++-- | The sealedbox C API uses C strings.+foreign import ccall "crypto_box_seal_open"+  c_box_seal_open :: Ptr CChar+                  -- ^ Message output buffer+                  -> Ptr CChar+                  -- ^ Constant ciphertext input buffer+                  -> CULLong+                  -- ^ Length of message input buffer+                  -> Ptr CChar+                  -- ^ Constant public key buffer+                  -> Ptr CChar+                  -- ^ Constant secret key buffer+                  -> IO CInt+                  -- ^ 0 for success, -1 for failure to decrypt
src/Crypto/Saltine/Internal/ByteSizes.hs view
@@ -1,263 +1,16 @@-{-# LANGUAGE ForeignFunctionInterface #-} -- |--- Module      : Crypto.Saltine.Core.ScalarMult+-- Module      : Crypto.Saltine.Internal.ByteSizes -- Copyright   : (c) Joseph Abrahamson 2013+--               (c) Max Amanshauser 2021 -- License     : MIT ----- Maintainer  : me@jspha.com+-- Maintainer  : max@lambdalifting.org -- Stability   : experimental -- Portability : non-portable ----- Various sizes------ While technically these sizes are hidden behind opaque newtype--- wrappers, they can be useful for computation and sizing and are--- thus exposed.------ As of @libsodium-4.1@ some of these sizes are not exported and thus--- are hardcoded here. This limitation should be removed in later--- versions of @libsodium@. module Crypto.Saltine.Internal.ByteSizes (--  auth,-  authKey,-  boxPK,-  boxSK,-  boxNonce,-  boxZero,-  boxBoxZero,-  boxMac,-  boxBeforeNM,-  sealedBox,-  onetime,-  onetimeKey,-  mult,-  multScalar,-  secretBoxKey,-  secretBoxNonce,-  secretBoxMac,-  secretBoxZero,-  secretBoxBoxZero,-  aead_xchacha20poly1305_ietf_ABYTES,-  sign,-  signPK,-  signSK,-  streamKey,-  streamNonce,-  hash,-  shorthash,-  shorthashKey   ) where -import Foreign.C---- Constants for--auth, authKey :: Int-boxPK, boxSK, boxNonce, boxZero, boxBoxZero :: Int-boxMac, boxBeforeNM, sealedBox :: Int-onetime, onetimeKey :: Int-mult, multScalar :: Int-secretBoxKey, secretBoxNonce, secretBoxMac, secretBoxZero, secretBoxBoxZero :: Int-sign, signPK, signSK :: Int-streamKey, streamNonce :: Int-hash, shorthash, shorthashKey :: Int----- Authentication--- | Size of a @crypto_auth@ authenticator.-auth    = fromIntegral c_crypto_auth_bytes--- | Size of a @crypto_auth@ authenticator key.-authKey = fromIntegral c_crypto_auth_keybytes---- Box--- | Size of a @crypto_box@ public key-boxPK       = fromIntegral c_crypto_box_publickeybytes--- | Size of a @crypto_box@ secret key-boxSK       = fromIntegral c_crypto_box_secretkeybytes--- | Size of a @crypto_box@ nonce-boxNonce    = fromIntegral c_crypto_box_noncebytes--- | Size of 0-padding prepended to messages before using @crypto_box@--- or after using @crypto_box_open@-boxZero     = fromIntegral c_crypto_box_zerobytes--- | Size of 0-padding prepended to ciphertext before using--- @crypto_box_open@ or after using @crypto_box@.-boxBoxZero  = fromIntegral c_crypto_box_boxzerobytes-boxMac      = fromIntegral c_crypto_box_macbytes--- | Size of a @crypto_box_beforenm@-generated combined key-boxBeforeNM =-  fromIntegral c_crypto_box_beforenmbytes---- SealedBox--- | Amount by which ciphertext is longer than plaintext--- in sealed boxes-sealedBox = fromIntegral c_crypto_box_sealbytes---- OneTimeAuth--- | Size of a @crypto_onetimeauth@ authenticator.-onetime    = fromIntegral c_crypto_onetimeauth_bytes--- | Size of a @crypto_onetimeauth@ authenticator key.-onetimeKey = fromIntegral c_crypto_onetimeauth_keybytes---- ScalarMult--- | Size of a group element string representation for--- @crypto_scalarmult@.-mult = fromIntegral c_crypto_scalarmult_bytes--- | Size of a integer string representation for @crypto_scalarmult@.-multScalar = fromIntegral c_crypto_scalarmult_scalarbytes---- SecretBox--- | Size of a @crypto_secretbox@ secret key-secretBoxKey     = fromIntegral c_crypto_secretbox_keybytes--- | Size of a @crypto_secretbox@ nonce-secretBoxNonce   = fromIntegral c_crypto_secretbox_noncebytes--- | Size of a @crypto_secretbox@ mac-secretBoxMac     = fromIntegral c_crypto_secretbox_macbytes--- | Size of 0-padding prepended to messages before using--- @crypto_secretbox@ or after using @crypto_secretbox_open@-secretBoxZero    = fromIntegral c_crypto_secretbox_zerobytes--- | Size of 0-padding prepended to ciphertext before using--- @crypto_secretbox_open@ or after using @crypto_secretbox@-secretBoxBoxZero = fromIntegral c_crypto_secretbox_boxzerobytes--aead_xchacha20poly1305_ietf_ABYTES :: Int-aead_xchacha20poly1305_ietf_ABYTES = fromIntegral c_crypto_aead_xchacha20poly1305_ietf_ABYTES ---- Signatures--- | The maximum size of a signature prepended to a message to form a--- signed message.-sign   = fromIntegral c_crypto_sign_bytes--- | The size of a public key for signing verification-signPK = fromIntegral c_crypto_sign_publickeybytes--- | The size of a secret key for signing-signSK = fromIntegral c_crypto_sign_secretkeybytes---- Streams--- | The size of a key for the cryptographic stream generation-streamKey   = fromIntegral c_crypto_stream_keybytes--- | The size of a nonce for the cryptographic stream generation-streamNonce = fromIntegral c_crypto_stream_noncebytes---- Hashes--- | The size of a hash resulting from--- 'Crypto.Saltine.Internal.Hash.hash'.-hash         = fromIntegral c_crypto_hash_bytes--- | The size of a keyed hash resulting from--- 'Crypto.Saltine.Internal.Hash.shorthash'.-shorthash    = fromIntegral c_crypto_shorthash_bytes--- | The size of a hashing key for the keyed hash function--- 'Crypto.Saltine.Internal.Hash.shorthash'.-shorthashKey = fromIntegral c_crypto_shorthash_keybytes---- src/libsodium/crypto_auth/crypto_auth.c-foreign import ccall "crypto_auth_bytes"-  c_crypto_auth_bytes :: CSize-foreign import ccall "crypto_auth_keybytes"-  c_crypto_auth_keybytes :: CSize---- src/libsodium/crypto_box/crypto_box.c-foreign import ccall "crypto_box_publickeybytes"-  c_crypto_box_publickeybytes :: CSize-foreign import ccall "crypto_box_secretkeybytes"-  c_crypto_box_secretkeybytes :: CSize-foreign import ccall "crypto_box_beforenmbytes"-  c_crypto_box_beforenmbytes :: CSize-foreign import ccall "crypto_box_noncebytes"-  c_crypto_box_noncebytes :: CSize-foreign import ccall "crypto_box_zerobytes"-  c_crypto_box_zerobytes :: CSize-foreign import ccall "crypto_box_boxzerobytes"-  c_crypto_box_boxzerobytes :: CSize-foreign import ccall "crypto_box_macbytes"-  c_crypto_box_macbytes :: CSize---- src/libsodium/crypto_box_seal.c-foreign import ccall "crypto_box_sealbytes"-  c_crypto_box_sealbytes :: CSize---- src/libsodium/crypto_onetimeauth/crypto_onetimeauth.c-foreign import ccall "crypto_onetimeauth_bytes"-  c_crypto_onetimeauth_bytes :: CSize-foreign import ccall "crypto_onetimeauth_keybytes"-  c_crypto_onetimeauth_keybytes :: CSize---- src/libsodium/crypto_scalarmult/crypto_scalarmult.c-foreign import ccall "crypto_scalarmult_bytes"-  c_crypto_scalarmult_bytes :: CSize-foreign import ccall "crypto_scalarmult_scalarbytes"-  c_crypto_scalarmult_scalarbytes :: CSize---- src/libsodium/crypto_secretbox/crypto_secretbox.c-foreign import ccall "crypto_secretbox_keybytes"-  c_crypto_secretbox_keybytes :: CSize-foreign import ccall "crypto_secretbox_noncebytes"-  c_crypto_secretbox_noncebytes :: CSize-foreign import ccall "crypto_secretbox_macbytes"-  c_crypto_secretbox_macbytes :: CSize-foreign import ccall "crypto_secretbox_zerobytes"-  c_crypto_secretbox_zerobytes :: CSize-foreign import ccall "crypto_secretbox_boxzerobytes"-  c_crypto_secretbox_boxzerobytes :: CSize---- src/libsodium/crypto_sign/crypto_sign.c-foreign import ccall "crypto_sign_bytes"-  c_crypto_sign_bytes :: CSize-foreign import ccall "crypto_sign_publickeybytes"-  c_crypto_sign_publickeybytes :: CSize-foreign import ccall "crypto_sign_secretkeybytes"-  c_crypto_sign_secretkeybytes :: CSize---- HARDCODED--- ------------- | The size of a @crypto_aead_tag@.------ HARDCODED to be @crypto_aead_xchacha20poly1305_ietf_ABYTES@ for now until Sodium--- exports the C constant (is a macro).-c_crypto_aead_xchacha20poly1305_ietf_ABYTES :: CSize-c_crypto_aead_xchacha20poly1305_ietf_ABYTES = 16---- | The size of a @crypto_stream@ or @crypto_stream_xor@--- key. HARDCODED to be @crypto_stream_xsalsa20@ for now until Sodium--- exports the C constant.-c_crypto_stream_keybytes :: CSize-c_crypto_stream_keybytes = 32---- | The size of a @crypto_stream@ or @crypto_stream_xor@--- nonce. HARDCODED to be @crypto_stream_xsalsa20@ for now until--- Sodium exports the C constant.-c_crypto_stream_noncebytes :: CSize-c_crypto_stream_noncebytes = 24---- | The size of a @crypto_hash@ output hash. HARDCODED to be--- @crypto_hash_sha512@ for now until Sodium exports the C constant.-c_crypto_hash_bytes :: CSize-c_crypto_hash_bytes = 64---- | The size of a @crypto_shorthash@ output hash. HARDCODED to be--- @crypto_shorthash_siphash24@ for now until Sodium exports the C--- constant.-c_crypto_shorthash_bytes :: CSize-c_crypto_shorthash_bytes = 8---- | The size of a @crypto_shorthash@ key. HARDCODED to be--- @crypto_shorthash_siphash24@ for now until Sodium exports the C--- constant.-c_crypto_shorthash_keybytes :: CSize-c_crypto_shorthash_keybytes = 16----- src/libsodium/crypto_stream/crypto_stream.c--- foreign import ccall "crypto_stream_keybytes"---   c_crypto_stream_keybytes :: CSize--- foreign import ccall "crypto_stream_noncebytes"---   c_crypto_stream_noncebytes :: CSize---- src/libsodium/crypto_shorthash/crypto_shorthash.c--- foreign import ccall "crypto_shorthash_bytes"---   c_crypto_shorthash_bytes :: CSize--- foreign import ccall "crypto_shorthash_keybytes"---   c_crypto_shorthash_keybytes :: CSize  -- Others -- ------
+ src/Crypto/Saltine/Internal/Hash.hs view
@@ -0,0 +1,182 @@+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving, DeriveGeneric, ForeignFunctionInterface #-}+-- |+-- Module      : Crypto.Saltine.Internal.Hash+-- Copyright   : (c) Max Amanshauser 2021+-- License     : MIT+--+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable+--+module Crypto.Saltine.Internal.Hash (+    hash_bytes+  , shorthash_bytes+  , shorthash_keybytes+  , generichash_bytes_max+  , generichash_keybytes_max+  , hash_sha256_bytes+  , c_hash+  , c_shorthash+  , c_generichash+  , c_hash_sha256+  , nullShKey+  , shorthash+  , ShorthashKey(..)+  , GenerichashKey(..)+  , GenerichashOutLen(..)+) where++import Control.DeepSeq+import Crypto.Saltine.Class+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Data.Data                    (Data, Typeable)+import Data.Hashable                (Hashable)+import Data.Monoid+import Foreign.C+import Foreign.Ptr+import GHC.Generics                 (Generic)++import qualified Data.ByteString       as S+import qualified Data.ByteString.Char8 as S8++-- | An opaque 'shorthash' cryptographic secret key.+newtype ShorthashKey = ShK { unShK :: ByteString } deriving (Ord, Hashable, Data, Typeable, Generic, NFData)+instance Eq ShorthashKey where+    ShK a == ShK b = U.compare a b+instance Show ShorthashKey where+    show k = "Hash.ShorthashKey {hashesTo = \"" <> (bin2hex . shorthash nullShKey $ encode k) <> "\"}"++-- | Used for our `Show` instances+nullShKey :: ShorthashKey+nullShKey = ShK (S8.replicate shorthash_keybytes '\NUL')++-- | Computes a very short, fast keyed hash.+-- This function is defined here to break circulat module imports+shorthash :: ShorthashKey+          -> ByteString+          -- ^ Message+          -> ByteString+          -- ^ Hash+shorthash (ShK k) m = snd . buildUnsafeByteString shorthash_bytes $ \ph ->+  constByteStrings [k, m] $ \[(pk, _), (pm, _)] ->+    c_shorthash ph pm (fromIntegral $ S.length m) pk++instance IsEncoding ShorthashKey where+  decode v = if S.length v == shorthash_keybytes+           then Just (ShK v)+           else Nothing+  {-# INLINE decode #-}+  encode (ShK v) = v+  {-# INLINE encode #-}++-- | An opaque 'generichash' cryptographic secret key.+newtype GenerichashKey = GhK { unGhK :: ByteString } deriving (Ord, Hashable, Data, Typeable, Generic, NFData)+instance Eq GenerichashKey where+    GhK a == GhK b = U.compare a b+instance Show GenerichashKey where+    show k = "Hash.GenerichashKey {hashesTo = \"" <> (bin2hex . shorthash nullShKey $ encode k) <> "\"}"++instance IsEncoding GenerichashKey where+  decode v = if S.length v <= generichash_keybytes_max+             then Just (GhK v)+             else Nothing+  {-# INLINE decode #-}+  encode (GhK v) = v+  {-# INLINE encode #-}++newtype GenerichashOutLen = GhOL { unGhOL :: Int } deriving (Eq, Ord, Hashable, Data, Typeable, Generic, NFData)++hash_bytes, shorthash_bytes, shorthash_keybytes, generichash_bytes_max, generichash_keybytes_max, hash_sha256_bytes :: Int++-- Hashes+-- | The size of a hash resulting from+-- 'Crypto.Saltine.Internal.Hash.hash'.+hash_bytes         = fromIntegral c_crypto_hash_bytes+-- | The size of a keyed hash resulting from+-- 'Crypto.Saltine.Internal.Hash.shorthash'.+shorthash_bytes    = fromIntegral c_crypto_shorthash_bytes+-- | The size of a hashing key for the keyed hash function+-- 'Crypto.Saltine.Internal.Hash.shorthash'.+shorthash_keybytes = fromIntegral c_crypto_shorthash_keybytes+-- | The maximum output size of the generic hash function+-- 'Crypto.Saltine.Core.Hash.generichash'+generichash_bytes_max = fromIntegral c_crypto_generichash_bytes_max+-- | The maximum key size of the generic hash function+-- 'Crypto.Saltine.Core.Hash.generichash'+generichash_keybytes_max = fromIntegral c_crypto_generichash_keybytes_max+-- | The size of a hash resulting from+-- 'Crypto.Saltine.Core.Hash.sha256'.+hash_sha256_bytes = fromIntegral c_crypto_hash_sha256_bytes++-- src/libsodium/crypto_generichash/crypto_generichash.c+foreign import ccall "crypto_generichash_bytes_max"+  c_crypto_generichash_bytes_max :: CSize+foreign import ccall "crypto_generichash_keybytes_max"+  c_crypto_generichash_keybytes_max :: CSize++-- src/libsodium/crypto_hash/crypto_hash.c+-- src/libsodium/include/sodium/crypto_hash.h+foreign import ccall "crypto_hash_bytes"+  c_crypto_hash_bytes :: CSize++-- src/libsodium/crypto_shorthash/crypto_shorthash.c+-- src/libsodium/include/sodium/crypto_shorthash.h+foreign import ccall "crypto_shorthash_bytes"+  c_crypto_shorthash_bytes :: CSize+foreign import ccall "crypto_shorthash_keybytes"+  c_crypto_shorthash_keybytes :: CSize++-- src/libsodium/crypto_hash/sha256/hash_sha256.c+-- src/libsodium/include/sodium/crypto_hash_sha256.h+foreign import ccall "crypto_hash_sha256_bytes"+  c_crypto_hash_sha256_bytes :: CSize+++foreign import ccall "crypto_hash"+  c_hash :: Ptr CChar+         -- ^ Output hash buffer+         -> Ptr CChar+         -- ^ Constant message buffer+         -> CULLong+         -- ^ Constant message buffer length+         -> IO CInt+         -- ^ Always 0++foreign import ccall "crypto_shorthash"+  c_shorthash :: Ptr CChar+              -- ^ Output hash buffer+              -> Ptr CChar+              -- ^ Constant message buffer+              -> CULLong+              -- ^ Message buffer length+              -> Ptr CChar+              -- ^ Constant Key buffer+              -> IO CInt+              -- ^ Always 0++foreign import ccall "crypto_generichash"+  c_generichash :: Ptr CChar+                -- ^ Output hash buffer+                -> CULLong+                -- ^ Output hash length+                -> Ptr CChar+                -- ^ Constant message buffer+                -> CULLong+                -- ^ Message buffer length+                -> Ptr CChar+                -- ^ Constant Key buffer+                -> CULLong+                -- ^ Key buffer length+                -> IO CInt+                -- ^ Always 0++foreign import ccall "crypto_hash_sha256"+  c_hash_sha256 :: Ptr CChar+                -- ^ Output hash buffer+                -> Ptr CChar+                -- ^ Constant message buffer+                -> CULLong+                -- ^ Message buffer length+                -> IO CInt+                -- ^ Always 0
+ src/Crypto/Saltine/Internal/OneTimeAuth.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving, DeriveGeneric, ForeignFunctionInterface #-}+-- |+-- Module      : Crypto.Saltine.Internal.OneTimeAuth+-- Copyright   : (c) Max Amanshauser 2021+-- License     : MIT+--+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable+--+module Crypto.Saltine.Internal.OneTimeAuth (+    onetimeauth_bytes+  , onetimeauth_keybytes+  , c_onetimeauth+  , c_onetimeauth_verify+  , Key(..)+  , Authenticator(..)+) where+++import Control.DeepSeq+import Crypto.Saltine.Class+import Crypto.Saltine.Core.Hash     (shorthash)+import Crypto.Saltine.Internal.Hash (nullShKey)+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Data.Data                    (Data, Typeable)+import Data.Hashable                (Hashable)+import Data.Monoid+import Foreign.C+import Foreign.Ptr+import GHC.Generics                 (Generic)++import qualified Data.ByteString as S++-- | An opaque 'auth' cryptographic key.+newtype Key = Key { unKey :: ByteString } deriving (Ord, Hashable, Data, Typeable, Generic, NFData)+instance Eq Key where+    Key a == Key b = U.compare a b+instance Show Key where+    show k = "OneTimeAuth.Key {hashesTo = \"" <> (bin2hex . shorthash nullShKey $ encode k) <> "\"}"++instance IsEncoding Key where+  decode v = if S.length v == onetimeauth_keybytes+           then Just (Key v)+           else Nothing+  {-# INLINE decode #-}+  encode (Key v) = v+  {-# INLINE encode #-}++-- | An opaque 'auth' authenticator.+newtype Authenticator = Au { unAu :: ByteString } deriving (Eq, Ord, Hashable, Data, Typeable, Generic, NFData)+instance Show Authenticator where+    show k = "OneTimeAuth.Authenticator " <> bin2hex (encode k)++instance IsEncoding Authenticator where+  decode v = if S.length v == onetimeauth_bytes+           then Just (Au v)+           else Nothing+  {-# INLINE decode #-}+  encode (Au v) = v+  {-# INLINE encode #-}+++onetimeauth_bytes, onetimeauth_keybytes :: Int++-- OneTimeAuth+-- | Size of a @crypto_onetimeauth@ authenticator.+onetimeauth_bytes    = fromIntegral c_crypto_onetimeauth_bytes+-- | Size of a @crypto_onetimeauth@ authenticator key.+onetimeauth_keybytes = fromIntegral c_crypto_onetimeauth_keybytes++-- src/libsodium/crypto_onetimeauth/crypto_onetimeauth.c+foreign import ccall "crypto_onetimeauth_bytes"+  c_crypto_onetimeauth_bytes :: CSize+foreign import ccall "crypto_onetimeauth_keybytes"+  c_crypto_onetimeauth_keybytes :: CSize+++foreign import ccall "crypto_onetimeauth"+  c_onetimeauth :: Ptr CChar+                -- ^ Authenticator output buffer+                -> Ptr CChar+                -- ^ Constant message buffer+                -> CULLong+                -- ^ Length of message buffer+                -> Ptr CChar+                -- ^ Constant key buffer+                -> IO CInt+                -- ^ Always 0++-- | We don't even include this in the IO monad since all of the+-- buffers are constant.+foreign import ccall "crypto_onetimeauth_verify"+  c_onetimeauth_verify :: Ptr CChar+                       -- ^ Constant authenticator buffer+                       -> Ptr CChar+                       -- ^ Constant message buffer+                       -> CULLong+                       -- ^ Length of message buffer+                       -> Ptr CChar+                       -- ^ Constant key buffer+                       -> CInt+                       -- ^ Success if 0, failure if -1
+ src/Crypto/Saltine/Internal/Password.hs view
@@ -0,0 +1,567 @@+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving, DeriveGeneric, ForeignFunctionInterface #-}++-- |+-- Module      : Crypto.Saltine.Internal.Password+-- Copyright   : (c) Promethea Raschke 2018+--                   Max Amanshauser 2021+-- License     : MIT+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable++module Crypto.Saltine.Internal.Password+  ( c_pwhash+  , c_pwhash_str+  , c_pwhash_str_verify+  , c_pwhash_str_needs_rehash++  , pwhash_alg_argon2i13+  , pwhash_alg_argon2id13+  , pwhash_alg_default+  , algorithm++  -- Default algorithm constants+  , pwhash_bytes_max+  , pwhash_bytes_min++  , pwhash_memlimit_interactive+  , pwhash_memlimit_moderate+  , pwhash_memlimit_sensitive+  , pwhash_memlimit_min+  , pwhash_memlimit_max++  , pwhash_opslimit_interactive+  , pwhash_opslimit_moderate+  , pwhash_opslimit_sensitive+  , pwhash_opslimit_min+  , pwhash_opslimit_max++  , pwhash_passwd_min+  , pwhash_passwd_max+  , pwhash_saltbytes+  , pwhash_strbytes+  , pwhash_strprefix++  -- Argon2i algorithm constants+  , pwhash_argon2i_bytes_max+  , pwhash_argon2i_bytes_min++  , pwhash_argon2i_memlimit_interactive+  , pwhash_argon2i_memlimit_moderate+  , pwhash_argon2i_memlimit_sensitive+  , pwhash_argon2i_memlimit_min+  , pwhash_argon2i_memlimit_max++  , pwhash_argon2i_opslimit_interactive+  , pwhash_argon2i_opslimit_moderate+  , pwhash_argon2i_opslimit_sensitive+  , pwhash_argon2i_opslimit_min+  , pwhash_argon2i_opslimit_max++  , pwhash_argon2i_passwd_min+  , pwhash_argon2i_passwd_max+  , pwhash_argon2i_saltbytes+  , pwhash_argon2i_strbytes+  , pwhash_argon2i_strprefix++  -- Argon2id algorithm constants+  , pwhash_argon2id_bytes_max+  , pwhash_argon2id_bytes_min++  , pwhash_argon2id_memlimit_interactive+  , pwhash_argon2id_memlimit_moderate+  , pwhash_argon2id_memlimit_sensitive+  , pwhash_argon2id_memlimit_min+  , pwhash_argon2id_memlimit_max++  , pwhash_argon2id_opslimit_interactive+  , pwhash_argon2id_opslimit_moderate+  , pwhash_argon2id_opslimit_sensitive+  , pwhash_argon2id_opslimit_min+  , pwhash_argon2id_opslimit_max++  , pwhash_argon2id_passwd_min+  , pwhash_argon2id_passwd_max+  , pwhash_argon2id_saltbytes+  , pwhash_argon2id_strbytes+  , pwhash_argon2id_strprefix++  , Salt(..)+  , PasswordHash(..)+  , Opslimit(..)+  , Memlimit(..)+  , Policy(..)+  , Algorithm(..)+  ) where++import Control.DeepSeq+import Crypto.Saltine.Class+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Data.Data                    (Data, Typeable)+import Data.Hashable                (Hashable)+import Data.Monoid+import Data.Text                    (Text)+import GHC.Generics                 (Generic)+import Foreign.C+import Foreign.Ptr++import qualified Data.ByteString    as S+import qualified Data.Text.Encoding as DTE+++-- | Salt for deriving keys from passwords+newtype Salt = Salt { unSalt :: ByteString } deriving (Ord, Data, Hashable, Typeable, Generic, NFData)+instance Eq Salt where+    Salt a == Salt b = U.compare a b+instance Show Salt where+    show k = "Password.Salt " <> bin2hex (encode k)++instance IsEncoding Salt where+  decode v = if S.length v == pwhash_saltbytes+           then Just (Salt v)+           else Nothing+  {-# INLINE decode #-}+  encode (Salt v) = v+  {-# INLINE encode #-}++-- | Verification string for stored passwords+-- This hash contains only printable characters, hence we can just derive Show.+newtype PasswordHash = PasswordHash { unPasswordHash :: Text } deriving (Ord, Data, Hashable, Typeable, Generic, Show, NFData)+-- Constant time Eq instance, just in case.+instance Eq PasswordHash where+    PasswordHash a == PasswordHash b = U.compare (DTE.encodeUtf8 a) (DTE.encodeUtf8 b)++-- | Wrapper type for the operations used by password hashing+newtype Opslimit = Opslimit { getOpslimit :: Int } deriving (Eq, Ord, Data, Hashable, Typeable, Generic, Show, NFData)++-- | Wrapper type for the memory used by password hashing+newtype Memlimit = Memlimit { getMemlimit :: Int } deriving (Eq, Ord, Data, Hashable, Typeable, Generic, Show, NFData)++-- | Wrapper for opslimit, memlimit and algorithm+data Policy = Policy+  { opsPolicy :: Opslimit+  , memPolicy :: Memlimit+  , algPolicy :: Algorithm+  } deriving (Eq, Ord, Data, Typeable, Generic, Show)+instance Hashable Policy++-- | Algorithms known to Libsodium, as an enum datatype+data Algorithm+  = DefaultAlgorithm+  | Argon2i13+  | Argon2id13+  deriving (Eq, Enum, Ord, Show, Generic, Data, Typeable, Bounded)+instance Hashable Algorithm++algorithm :: Algorithm -> CInt+algorithm DefaultAlgorithm = fromIntegral pwhash_alg_default+algorithm Argon2i13        = fromIntegral pwhash_alg_argon2i13+algorithm Argon2id13       = fromIntegral pwhash_alg_argon2id13++-- | Lets libsodium pick a hashing algorithm+pwhash_alg_default :: Int+pwhash_alg_default = fromIntegral c_crypto_pwhash_alg_default+-- | version 1.3 of the Argon2i algorithm+pwhash_alg_argon2i13 :: Int+pwhash_alg_argon2i13 = fromIntegral c_crypto_pwhash_alg_argon2i13+-- | version 1.3 of the Argon2id algorithm+pwhash_alg_argon2id13 :: Int+pwhash_alg_argon2id13 = fromIntegral c_crypto_pwhash_alg_argon2id13++-- | Constants for the default algorithm+-- | Minimum output length for key derivation (16 (128 bits)).+pwhash_bytes_min :: Int+pwhash_bytes_min = fromIntegral c_crypto_pwhash_bytes_min+-- | Maximum output length for key derivation.+pwhash_bytes_max :: Int+pwhash_bytes_max = fromIntegral c_crypto_pwhash_bytes_max++-- | Minimum allowed memory limit for password hashing+pwhash_memlimit_min :: Int+pwhash_memlimit_min = fromIntegral c_crypto_pwhash_memlimit_min+-- | Maximum allowed memory limit for password hashing+pwhash_memlimit_max :: Int+pwhash_memlimit_max = fromIntegral c_crypto_pwhash_memlimit_max+-- | Constant for currently 64MB memory+pwhash_memlimit_interactive :: Int+pwhash_memlimit_interactive = fromIntegral c_crypto_pwhash_memlimit_interactive+-- | Constant for currently 256MB memory+pwhash_memlimit_moderate :: Int+pwhash_memlimit_moderate = fromIntegral c_crypto_pwhash_memlimit_moderate+-- | Constant for currently 1024MB memory+pwhash_memlimit_sensitive :: Int+pwhash_memlimit_sensitive = fromIntegral c_crypto_pwhash_memlimit_sensitive++-- | Minimum allowed number of computations for password hashing+pwhash_opslimit_min :: Int+pwhash_opslimit_min = fromIntegral c_crypto_pwhash_opslimit_min+-- | Maximum allowed number of computations for password hashing+pwhash_opslimit_max :: Int+pwhash_opslimit_max = fromIntegral c_crypto_pwhash_opslimit_max++-- | Constant for relatively fast hashing+pwhash_opslimit_interactive :: Int+pwhash_opslimit_interactive = fromIntegral c_crypto_pwhash_opslimit_interactive+-- | Constant for moderately fast hashing+pwhash_opslimit_moderate :: Int+pwhash_opslimit_moderate = fromIntegral c_crypto_pwhash_opslimit_moderate+-- | Constant for relatively slow hashing+pwhash_opslimit_sensitive :: Int+pwhash_opslimit_sensitive = fromIntegral c_crypto_pwhash_opslimit_sensitive++-- | Minimum number of characters in password for key derivation+pwhash_passwd_min :: Int+pwhash_passwd_min = fromIntegral c_crypto_pwhash_passwd_min+-- | Maximum number of characters in password for key derivation+pwhash_passwd_max :: Int+pwhash_passwd_max = fromIntegral c_crypto_pwhash_passwd_max++-- | Size of salt+pwhash_saltbytes :: Int+pwhash_saltbytes = fromIntegral c_crypto_pwhash_saltbytes+-- | (Maximum) size of password hashing output+pwhash_strbytes :: Int+pwhash_strbytes = fromIntegral c_crypto_pwhash_strbytes+-- string that hashes with this algorithm are prefixed with+pwhash_strprefix :: Int+pwhash_strprefix = fromIntegral c_crypto_pwhash_strprefix+++-- | Constants for Argon2ID+-- | Minimum output length for key derivation (= 16 (128 bits)).+pwhash_argon2id_bytes_min :: Int+pwhash_argon2id_bytes_min = fromIntegral c_crypto_pwhash_argon2id_bytes_min+-- | Maximum output length for key derivation.+pwhash_argon2id_bytes_max :: Int+pwhash_argon2id_bytes_max = fromIntegral c_crypto_pwhash_argon2id_bytes_max++-- | Minimum allowed memory limit for password hashing+pwhash_argon2id_memlimit_min :: Int+pwhash_argon2id_memlimit_min = fromIntegral c_crypto_pwhash_argon2id_memlimit_min+-- | Maximum allowed memory limit for password hashing+pwhash_argon2id_memlimit_max :: Int+pwhash_argon2id_memlimit_max = fromIntegral c_crypto_pwhash_argon2id_memlimit_max+-- | Constant for currently 64MB memory+pwhash_argon2id_memlimit_interactive :: Int+pwhash_argon2id_memlimit_interactive = fromIntegral c_crypto_pwhash_argon2id_memlimit_interactive+-- | Constant for currently 256MB memory+pwhash_argon2id_memlimit_moderate :: Int+pwhash_argon2id_memlimit_moderate = fromIntegral c_crypto_pwhash_argon2id_memlimit_moderate+-- | Constant for currently 1024MB memory+pwhash_argon2id_memlimit_sensitive :: Int+pwhash_argon2id_memlimit_sensitive = fromIntegral c_crypto_pwhash_argon2id_memlimit_sensitive++-- | Minimum allowed number of computations for password hashing+pwhash_argon2id_opslimit_min :: Int+pwhash_argon2id_opslimit_min = fromIntegral c_crypto_pwhash_argon2id_opslimit_min+-- | Maximum allowed number of computations for password hashing+pwhash_argon2id_opslimit_max :: Int+pwhash_argon2id_opslimit_max = fromIntegral c_crypto_pwhash_argon2id_opslimit_max++-- | Constant for relatively fast hashing+pwhash_argon2id_opslimit_interactive :: Int+pwhash_argon2id_opslimit_interactive = fromIntegral c_crypto_pwhash_argon2id_opslimit_interactive+-- | Constant for moderately fast hashing+pwhash_argon2id_opslimit_moderate :: Int+pwhash_argon2id_opslimit_moderate = fromIntegral c_crypto_pwhash_argon2id_opslimit_moderate+-- | Constant for relatively slow hashing+pwhash_argon2id_opslimit_sensitive :: Int+pwhash_argon2id_opslimit_sensitive = fromIntegral c_crypto_pwhash_argon2id_opslimit_sensitive++-- | Minimum number of characters in password for key derivation+pwhash_argon2id_passwd_min :: Int+pwhash_argon2id_passwd_min = fromIntegral c_crypto_pwhash_argon2id_passwd_min+-- | Maximum number of characters in password for key derivation+pwhash_argon2id_passwd_max :: Int+pwhash_argon2id_passwd_max = fromIntegral c_crypto_pwhash_argon2id_passwd_max++-- | Size of salt+pwhash_argon2id_saltbytes :: Int+pwhash_argon2id_saltbytes = fromIntegral c_crypto_pwhash_argon2id_saltbytes+-- | (Maximum) size of password hashing output+pwhash_argon2id_strbytes :: Int+pwhash_argon2id_strbytes = fromIntegral c_crypto_pwhash_argon2id_strbytes+-- string that hashes with this algorithm are prefixed with+pwhash_argon2id_strprefix :: Int+pwhash_argon2id_strprefix = fromIntegral c_crypto_pwhash_argon2id_strprefix++-- | Constants for ARGON2I+-- | Minimum output length for key derivation (= 16 (128 bits)).+pwhash_argon2i_bytes_min :: Int+pwhash_argon2i_bytes_min = fromIntegral c_crypto_pwhash_argon2i_bytes_min+-- | Maximum output length for key derivation.+pwhash_argon2i_bytes_max :: Int+pwhash_argon2i_bytes_max = fromIntegral c_crypto_pwhash_argon2i_bytes_max++-- | Minimum allowed memory limit for password hashing+pwhash_argon2i_memlimit_min :: Int+pwhash_argon2i_memlimit_min = fromIntegral c_crypto_pwhash_argon2i_memlimit_min+-- | Maximum allowed memory limit for password hashing+pwhash_argon2i_memlimit_max :: Int+pwhash_argon2i_memlimit_max = fromIntegral c_crypto_pwhash_argon2i_memlimit_max+-- | Constant for currently 64MB memory+pwhash_argon2i_memlimit_interactive :: Int+pwhash_argon2i_memlimit_interactive = fromIntegral c_crypto_pwhash_argon2i_memlimit_interactive+-- | Constant for currently 256MB memory+pwhash_argon2i_memlimit_moderate :: Int+pwhash_argon2i_memlimit_moderate = fromIntegral c_crypto_pwhash_argon2i_memlimit_moderate+-- | Constant for currently 1024MB memory+pwhash_argon2i_memlimit_sensitive :: Int+pwhash_argon2i_memlimit_sensitive = fromIntegral c_crypto_pwhash_argon2i_memlimit_sensitive++-- | Minimum allowed number of computations for password hashing+pwhash_argon2i_opslimit_min :: Int+pwhash_argon2i_opslimit_min = fromIntegral c_crypto_pwhash_argon2i_opslimit_min+-- | Maximum allowed number of computations for password hashing+pwhash_argon2i_opslimit_max :: Int+pwhash_argon2i_opslimit_max = fromIntegral c_crypto_pwhash_argon2i_opslimit_max++-- | Constant for relatively fast hashing+pwhash_argon2i_opslimit_interactive :: Int+pwhash_argon2i_opslimit_interactive = fromIntegral c_crypto_pwhash_argon2i_opslimit_interactive+-- | Constant for moderately fast hashing+pwhash_argon2i_opslimit_moderate :: Int+pwhash_argon2i_opslimit_moderate = fromIntegral c_crypto_pwhash_argon2i_opslimit_moderate+-- | Constant for relatively slow hashing+pwhash_argon2i_opslimit_sensitive :: Int+pwhash_argon2i_opslimit_sensitive = fromIntegral c_crypto_pwhash_argon2i_opslimit_sensitive++-- | Minimum number of characters in password for key derivation+pwhash_argon2i_passwd_min :: Int+pwhash_argon2i_passwd_min = fromIntegral c_crypto_pwhash_argon2i_passwd_min+-- | Maximum number of characters in password for key derivation+pwhash_argon2i_passwd_max :: Int+pwhash_argon2i_passwd_max = fromIntegral c_crypto_pwhash_argon2i_passwd_max++-- | Size of salt+pwhash_argon2i_saltbytes :: Int+pwhash_argon2i_saltbytes = fromIntegral c_crypto_pwhash_argon2i_saltbytes+-- | (Maximum) size of password hashing output+pwhash_argon2i_strbytes :: Int+pwhash_argon2i_strbytes = fromIntegral c_crypto_pwhash_argon2i_strbytes+-- string that hashes with this algorithm are prefixed with+pwhash_argon2i_strprefix :: Int+pwhash_argon2i_strprefix = fromIntegral c_crypto_pwhash_argon2i_strprefix++++foreign import ccall "crypto_pwhash"+  c_pwhash+        :: Ptr CChar+        -- ^ Derived key output buffer+        -> CULLong+        -- ^ Derived key length+        -> Ptr CChar+        -- ^ Password input buffer+        -> CULLong+        -- ^ Password length+        -> Ptr CChar+        -- ^ Salt input buffer+        -> CULLong+        -- ^ Operation limit+        -> CSize+        -- ^ Memory usage limit+        -> CInt+        -- ^ Algorithm+        -> IO CInt++foreign import ccall "crypto_pwhash_str"+  c_pwhash_str+        :: Ptr CChar+        -- ^ Hashed password output buffer+        -> Ptr CChar+        -- ^ Password input buffer+        -> CULLong+        -- ^ Password length+        -> CULLong+        -- ^ Operation limit+        -> CSize+        -- ^ Memory usage limit+        -> IO CInt++foreign import ccall "crypto_pwhash_str_verify"+  c_pwhash_str_verify+        :: Ptr CChar+        -- ^ Hashed password input buffer+        -> Ptr CChar+        -- ^ Password input buffer+        -> CULLong+        -- ^ Password length+        -> IO CInt++foreign import ccall "crypto_pwhash_str_needs_rehash"+  c_pwhash_str_needs_rehash+        :: Ptr CChar+        -- ^ Hashed password input buffer+        -> CULLong+        -- ^ Operation limit+        -> CSize+        -- ^ Memory usage limit+        -> IO CInt++foreign import ccall "crypto_pwhash_alg_argon2id13"+  c_crypto_pwhash_alg_argon2id13 :: CSize++foreign import ccall "crypto_pwhash_alg_argon2i13"+  c_crypto_pwhash_alg_argon2i13 :: CSize++foreign import ccall "crypto_pwhash_alg_default"+  c_crypto_pwhash_alg_default :: CSize++-- Constants for the default algorithm+foreign import ccall "crypto_pwhash_bytes_min"+  c_crypto_pwhash_bytes_min :: CSize++foreign import ccall "crypto_pwhash_bytes_max"+  c_crypto_pwhash_bytes_max :: CSize++foreign import ccall "crypto_pwhash_memlimit_min"+  c_crypto_pwhash_memlimit_min :: CSize++foreign import ccall "crypto_pwhash_memlimit_max"+  c_crypto_pwhash_memlimit_max :: CSize++foreign import ccall "crypto_pwhash_memlimit_interactive"+  c_crypto_pwhash_memlimit_interactive :: CSize++foreign import ccall "crypto_pwhash_memlimit_moderate"+  c_crypto_pwhash_memlimit_moderate :: CSize++foreign import ccall "crypto_pwhash_memlimit_sensitive"+  c_crypto_pwhash_memlimit_sensitive :: CSize++foreign import ccall "crypto_pwhash_opslimit_min"+  c_crypto_pwhash_opslimit_min :: CSize++foreign import ccall "crypto_pwhash_opslimit_max"+  c_crypto_pwhash_opslimit_max :: CSize++foreign import ccall "crypto_pwhash_opslimit_interactive"+  c_crypto_pwhash_opslimit_interactive :: CSize++foreign import ccall "crypto_pwhash_opslimit_moderate"+  c_crypto_pwhash_opslimit_moderate :: CSize++foreign import ccall "crypto_pwhash_opslimit_sensitive"+  c_crypto_pwhash_opslimit_sensitive :: CSize++foreign import ccall "crypto_pwhash_passwd_min"+  c_crypto_pwhash_passwd_min :: CSize++foreign import ccall "crypto_pwhash_passwd_max"+  c_crypto_pwhash_passwd_max :: CSize++foreign import ccall "crypto_pwhash_saltbytes"+  c_crypto_pwhash_saltbytes :: CSize++foreign import ccall "crypto_pwhash_strbytes"+  c_crypto_pwhash_strbytes :: CSize++foreign import ccall "crypto_pwhash_strprefix"+  c_crypto_pwhash_strprefix :: CSize++-- Constants for ARGON2ID (currently default)+foreign import ccall "crypto_pwhash_argon2id_bytes_min"+  c_crypto_pwhash_argon2id_bytes_min :: CSize++foreign import ccall "crypto_pwhash_argon2id_bytes_max"+  c_crypto_pwhash_argon2id_bytes_max :: CSize++foreign import ccall "crypto_pwhash_argon2id_memlimit_min"+  c_crypto_pwhash_argon2id_memlimit_min :: CSize++foreign import ccall "crypto_pwhash_argon2id_memlimit_max"+  c_crypto_pwhash_argon2id_memlimit_max :: CSize++foreign import ccall "crypto_pwhash_argon2id_memlimit_interactive"+  c_crypto_pwhash_argon2id_memlimit_interactive :: CSize++foreign import ccall "crypto_pwhash_argon2id_memlimit_moderate"+  c_crypto_pwhash_argon2id_memlimit_moderate :: CSize++foreign import ccall "crypto_pwhash_argon2id_memlimit_sensitive"+  c_crypto_pwhash_argon2id_memlimit_sensitive :: CSize++foreign import ccall "crypto_pwhash_argon2id_opslimit_min"+  c_crypto_pwhash_argon2id_opslimit_min :: CSize++foreign import ccall "crypto_pwhash_argon2id_opslimit_max"+  c_crypto_pwhash_argon2id_opslimit_max :: CSize++foreign import ccall "crypto_pwhash_argon2id_opslimit_interactive"+  c_crypto_pwhash_argon2id_opslimit_interactive :: CSize++foreign import ccall "crypto_pwhash_argon2id_opslimit_moderate"+  c_crypto_pwhash_argon2id_opslimit_moderate :: CSize++foreign import ccall "crypto_pwhash_argon2id_opslimit_sensitive"+  c_crypto_pwhash_argon2id_opslimit_sensitive :: CSize++foreign import ccall "crypto_pwhash_argon2id_passwd_min"+  c_crypto_pwhash_argon2id_passwd_min :: CSize++foreign import ccall "crypto_pwhash_argon2id_passwd_max"+  c_crypto_pwhash_argon2id_passwd_max :: CSize++foreign import ccall "crypto_pwhash_argon2id_saltbytes"+  c_crypto_pwhash_argon2id_saltbytes :: CSize++foreign import ccall "crypto_pwhash_argon2id_strbytes"+  c_crypto_pwhash_argon2id_strbytes :: CSize++foreign import ccall "crypto_pwhash_argon2id_strprefix"+  c_crypto_pwhash_argon2id_strprefix :: CSize+++-- Constants for ARGON2I+foreign import ccall "crypto_pwhash_argon2i_bytes_min"+  c_crypto_pwhash_argon2i_bytes_min :: CSize++foreign import ccall "crypto_pwhash_argon2i_bytes_max"+  c_crypto_pwhash_argon2i_bytes_max :: CSize++foreign import ccall "crypto_pwhash_argon2i_memlimit_min"+  c_crypto_pwhash_argon2i_memlimit_min :: CSize++foreign import ccall "crypto_pwhash_argon2i_memlimit_max"+  c_crypto_pwhash_argon2i_memlimit_max :: CSize++foreign import ccall "crypto_pwhash_argon2i_memlimit_interactive"+  c_crypto_pwhash_argon2i_memlimit_interactive :: CSize++foreign import ccall "crypto_pwhash_argon2i_memlimit_moderate"+  c_crypto_pwhash_argon2i_memlimit_moderate :: CSize++foreign import ccall "crypto_pwhash_argon2i_memlimit_sensitive"+  c_crypto_pwhash_argon2i_memlimit_sensitive :: CSize++foreign import ccall "crypto_pwhash_argon2i_opslimit_min"+  c_crypto_pwhash_argon2i_opslimit_min :: CSize++foreign import ccall "crypto_pwhash_argon2i_opslimit_max"+  c_crypto_pwhash_argon2i_opslimit_max :: CSize++foreign import ccall "crypto_pwhash_argon2i_opslimit_interactive"+  c_crypto_pwhash_argon2i_opslimit_interactive :: CSize++foreign import ccall "crypto_pwhash_argon2i_opslimit_moderate"+  c_crypto_pwhash_argon2i_opslimit_moderate :: CSize++foreign import ccall "crypto_pwhash_argon2i_opslimit_sensitive"+  c_crypto_pwhash_argon2i_opslimit_sensitive :: CSize++foreign import ccall "crypto_pwhash_argon2i_passwd_min"+  c_crypto_pwhash_argon2i_passwd_min :: CSize++foreign import ccall "crypto_pwhash_argon2i_passwd_max"+  c_crypto_pwhash_argon2i_passwd_max :: CSize++foreign import ccall "crypto_pwhash_argon2i_saltbytes"+  c_crypto_pwhash_argon2i_saltbytes :: CSize++foreign import ccall "crypto_pwhash_argon2i_strbytes"+  c_crypto_pwhash_argon2i_strbytes :: CSize++foreign import ccall "crypto_pwhash_argon2i_strprefix"+  c_crypto_pwhash_argon2i_strprefix :: CSize
+ src/Crypto/Saltine/Internal/ScalarMult.hs view
@@ -0,0 +1,91 @@+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving, DeriveGeneric, ForeignFunctionInterface #-}+-- |+-- Module      : Crypto.Saltine.Internal.ScalarMult+-- Copyright   : (c) Max Amanshauser 2021+-- License     : MIT+--+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable+--+module Crypto.Saltine.Internal.ScalarMult (+    scalarmult_bytes+  , scalarmult_scalarbytes+  , c_scalarmult+  , c_scalarmult_base+  , GroupElement(..)+  , Scalar(..)+) where++import Control.DeepSeq+import Crypto.Saltine.Class+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Data.Data                    (Data, Typeable)+import Data.Hashable                (Hashable)+import Foreign.C+import Foreign.Ptr+import GHC.Generics                 (Generic)++import qualified Data.ByteString as S+++-- | A group element.+newtype GroupElement = GE { unGE :: ByteString } deriving (Eq, Ord, Hashable, Data, Typeable, Generic, NFData)+instance Show GroupElement where+    show = bin2hex . encode++instance IsEncoding GroupElement where+  decode v = if S.length v == scalarmult_bytes+           then Just (GE v)+           else Nothing+  {-# INLINE decode #-}+  encode (GE v) = v+  {-# INLINE encode #-}++-- | A scalar integer.+newtype Scalar       = Sc { unSc :: ByteString } deriving (Eq, Ord, Hashable, Data, Typeable, Generic, NFData)+instance Show Scalar where+    show = bin2hex . encode++instance IsEncoding Scalar where+  decode v = if S.length v == scalarmult_scalarbytes+           then Just (Sc v)+           else Nothing+  {-# INLINE decode #-}+  encode (Sc v) = v+  {-# INLINE encode #-}+++scalarmult_bytes, scalarmult_scalarbytes :: Int++-- ScalarMult+-- | Size of a group element string representation for+-- @crypto_scalarmult@.+scalarmult_bytes = fromIntegral c_crypto_scalarmult_bytes+-- | Size of a integer string representation for @crypto_scalarmult@.+scalarmult_scalarbytes = fromIntegral c_crypto_scalarmult_scalarbytes++-- src/libsodium/crypto_scalarmult/crypto_scalarmult.c+foreign import ccall "crypto_scalarmult_bytes"+  c_crypto_scalarmult_bytes :: CSize+foreign import ccall "crypto_scalarmult_scalarbytes"+  c_crypto_scalarmult_scalarbytes :: CSize++foreign import ccall "crypto_scalarmult"+  c_scalarmult :: Ptr CChar+               -- ^ Output group element buffer+               -> Ptr CChar+               -- ^ Input integer buffer+               -> Ptr CChar+               -- ^ Input group element buffer+               -> IO CInt+               -- ^ Always 0++foreign import ccall "crypto_scalarmult_base"+  c_scalarmult_base :: Ptr CChar+                    -- ^ Output group element buffer+                    -> Ptr CChar+                    -- ^ Input integer buffer+                    -> IO CInt+                    -- ^ Always 0
+ src/Crypto/Saltine/Internal/SecretBox.hs view
@@ -0,0 +1,179 @@+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving, DeriveGeneric, ForeignFunctionInterface #-}+-- |+-- Module      : Crypto.Saltine.Internal.SecretBox+-- Copyright   : (c) Max Amanshauser 2021+-- License     : MIT+--+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable+--+module Crypto.Saltine.Internal.SecretBox (+  secretbox_keybytes,+  secretbox_noncebytes,+  secretbox_macbytes,+  secretbox_zerobytes,+  secretbox_boxzerobytes,+  c_secretbox,+  c_secretbox_detached,+  c_secretbox_open,+  c_secretbox_open_detached,+  Key(..),+  Nonce(..),+  Authenticator(..)+) where++import Control.DeepSeq              (NFData)+import Crypto.Saltine.Class+import Crypto.Saltine.Core.Hash     (shorthash)+import Crypto.Saltine.Internal.Hash (nullShKey)+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Data.Data                    (Data, Typeable)+import Data.Hashable                (Hashable)+import Data.Monoid+import Foreign.C+import Foreign.Ptr+import GHC.Generics                 (Generic)++import qualified Data.ByteString as S++secretbox_keybytes, secretbox_noncebytes, secretbox_macbytes, secretbox_zerobytes, secretbox_boxzerobytes :: Int++-- | An opaque 'secretbox' cryptographic key.+newtype Key = Key { unKey :: ByteString } deriving (Ord, Hashable, Data, Typeable, Generic, NFData)+instance Eq Key where+    Key a == Key b = U.compare a b+instance Show Key where+    show k = "SecretBox.Key {hashesTo = \"" <> (bin2hex . shorthash nullShKey $ encode k) <> "\"}"++instance IsEncoding Key where+  decode v = if S.length v == secretbox_keybytes+           then Just (Key v)+           else Nothing+  {-# INLINE decode #-}+  encode (Key v) = v+  {-# INLINE encode #-}++-- | An opaque 'secretbox' nonce.+newtype Nonce = Nonce { unNonce :: ByteString } deriving (Eq, Ord, Hashable, Data, Typeable, Generic, NFData)+instance Show Nonce where+    show k = "SecretBox.Nonce " <> bin2hex (encode k)++instance IsEncoding Nonce where+  decode v = if S.length v == secretbox_noncebytes+           then Just (Nonce v)+           else Nothing+  {-# INLINE decode #-}+  encode (Nonce v) = v+  {-# INLINE encode #-}++instance IsNonce Nonce where+  zero            = Nonce (S.replicate secretbox_noncebytes 0)+  nudge (Nonce n) = Nonce (nudgeBS n)+++-- | An Authenticator for a Message+newtype Authenticator = Au { unAu :: ByteString } deriving (Eq, Ord, Data, Typeable, Hashable, Generic, NFData)+instance Show Authenticator where+    show k = "Sign.Authenticator " <> bin2hex (encode k)++instance IsEncoding Authenticator where+  decode v = if S.length v == secretbox_macbytes+           then Just (Au v)+           else Nothing+  {-# INLINE decode #-}+  encode (Au v) = v+  {-# INLINE encode #-}+++-- | Size of a @crypto_secretbox@ secret key+secretbox_keybytes     = fromIntegral c_crypto_secretbox_keybytes+-- | Size of a @crypto_secretbox@ nonce+secretbox_noncebytes   = fromIntegral c_crypto_secretbox_noncebytes+-- | Size of a @crypto_secretbox@ mac+secretbox_macbytes     = fromIntegral c_crypto_secretbox_macbytes+-- | Size of 0-padding prepended to messages before using+-- @crypto_secretbox@ or after using @crypto_secretbox_open@+secretbox_zerobytes    = fromIntegral c_crypto_secretbox_zerobytes+-- | Size of 0-padding prepended to ciphertext before using+-- @crypto_secretbox_open@ or after using @crypto_secretbox@+secretbox_boxzerobytes = fromIntegral c_crypto_secretbox_boxzerobytes++-- src/libsodium/crypto_secretbox/crypto_secretbox.c+foreign import ccall "crypto_secretbox_keybytes"+  c_crypto_secretbox_keybytes :: CSize+foreign import ccall "crypto_secretbox_noncebytes"+  c_crypto_secretbox_noncebytes :: CSize+foreign import ccall "crypto_secretbox_macbytes"+  c_crypto_secretbox_macbytes :: CSize+foreign import ccall "crypto_secretbox_zerobytes"+  c_crypto_secretbox_zerobytes :: CSize+foreign import ccall "crypto_secretbox_boxzerobytes"+  c_crypto_secretbox_boxzerobytes :: CSize++-- | The secretbox C API uses 0-padded C strings. Always returns 0.+foreign import ccall "crypto_secretbox"+  c_secretbox+    :: Ptr CChar+    -- ^ Cipher 0-padded output buffer+    -> Ptr CChar+    -- ^ Constant 0-padded message input buffer+    -> CULLong+    -- ^ Length of message input buffer (incl. 0s)+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt++-- | The secretbox_detached C API uses C strings. Always returns 0.+foreign import ccall "crypto_secretbox_detached"+  c_secretbox_detached+    :: Ptr CChar+    -- ^ Ciphertext output buffer+    -> Ptr CChar+    -- ^ Authentication tag output buffer+    -> Ptr CChar+    -- ^ Constant message input buffer+    -> CULLong+    -- ^ Length of message input buffer (incl. 0s)+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt++-- | The secretbox C API uses 0-padded C strings. Returns 0 if+-- successful or -1 if verification failed.+foreign import ccall "crypto_secretbox_open"+  c_secretbox_open+    :: Ptr CChar+    -- ^ Message 0-padded output buffer+    -> Ptr CChar+    -- ^ Constant 0-padded message input buffer+    -> CULLong+    -- ^ Length of message input buffer (incl. 0s)+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt++-- | The secretbox C API uses C strings. Returns 0 if+-- successful or -1 if verification failed.+foreign import ccall "crypto_secretbox_open_detached"+  c_secretbox_open_detached+    :: Ptr CChar+    -- ^ Message output buffer+    -> Ptr CChar+    -- ^ Constant ciphertext input buffer+    -> Ptr CChar+    -- ^ Constant auth tag input buffer+    -> CULLong+    -- ^ Length of ciphertext input buffer+    -> Ptr CChar+    -- ^ Constant nonce buffer+    -> Ptr CChar+    -- ^ Constant key buffer+    -> IO CInt
+ src/Crypto/Saltine/Internal/Sign.hs view
@@ -0,0 +1,198 @@+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving, DeriveGeneric, ForeignFunctionInterface #-}+-- |+-- Module      : Crypto.Saltine.Internal.Sign+-- Copyright   : (c) Max Amanshauser 2021+-- License     : MIT+--+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable+--+module Crypto.Saltine.Internal.Sign (+    sign_bytes+  , sign_publickeybytes+  , sign_secretkeybytes+  , c_sign_keypair+  , c_sign+  , c_sign_open+  , c_sign_detached+  , c_sign_verify_detached+  , c_sign_ed25519_pk_to_curve25519+  , c_sign_ed25519_sk_to_curve25519+  , SecretKey(..)+  , PublicKey(..)+  , Keypair(..)+  , Signature(..)+) where++import Control.DeepSeq              (NFData)+import Crypto.Saltine.Class+import Crypto.Saltine.Core.Hash     (shorthash)+import Crypto.Saltine.Internal.Hash (nullShKey)+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Data.Data                    (Data, Typeable)+import Data.Hashable                (Hashable)+import Data.Monoid+import Foreign.C+import Foreign.Ptr+import GHC.Generics                 (Generic)++import qualified Data.ByteString as S+++-- | An opaque 'box' cryptographic secret key.+newtype SecretKey = SK { unSK :: ByteString } deriving (Ord, Hashable, Data, Typeable, Generic, NFData)+instance Eq SecretKey where+    SK a == SK b = U.compare a b+instance Show SecretKey where+    show k = "Sign.SecretKey {hashesTo = \"" <> (bin2hex . shorthash nullShKey $ encode k) <> "\"}"++instance IsEncoding SecretKey where+  decode v = if S.length v == sign_secretkeybytes+           then Just (SK v)+           else Nothing+  {-# INLINE decode #-}+  encode (SK v) = v+  {-# INLINE encode #-}++-- | An opaque 'box' cryptographic public key.+newtype PublicKey = PK { unPK :: ByteString } deriving (Ord, Data, Typeable, Hashable, Generic, NFData)+instance Eq PublicKey where+    PK a == PK b = U.compare a b+instance Show PublicKey where+    show k = "Sign.PublicKey {hashesTo = \"" <> (bin2hex . shorthash nullShKey $ encode k) <> "\"}"++instance IsEncoding PublicKey where+  decode v = if S.length v == sign_publickeybytes+           then Just (PK v)+           else Nothing+  {-# INLINE decode #-}+  encode (PK v) = v+  {-# INLINE encode #-}++-- | A convenience type for keypairs+data Keypair = Keypair {+    secretKey :: SecretKey+  , publicKey :: PublicKey+} deriving (Ord, Data, Typeable, Generic)++instance Eq Keypair where+    kp1 == kp2 = U.compare (encode $ secretKey kp1) (encode $ secretKey kp2)+            !&&! U.compare (encode $ publicKey kp1) (encode $ publicKey kp2)++instance Show Keypair where+    show k = "Sign.Keypair {secretKey = " <> show (secretKey k) <> ", publicKey = " <> show (publicKey k) <> "}"++instance Hashable Keypair+instance NFData   Keypair+++-- | A signature for a Message+newtype Signature = Signature { unSignature :: ByteString } deriving (Ord, Data, Typeable, Hashable, Generic, NFData)+instance Eq Signature where+    Signature a == Signature b = U.compare a b+instance Show Signature where+    show k = "Sign.Signature " <> bin2hex (encode k)++-- | Actual signatures may be shorter, but not when generated with saltine.+instance IsEncoding Signature where+  decode v = if S.length v == sign_bytes+           then Just (Signature v)+           else Nothing+  {-# INLINE decode #-}+  encode (Signature s) = s+  {-# INLINE encode #-}++sign_bytes, sign_publickeybytes, sign_secretkeybytes :: Int++-- Signatures+-- | The maximum size of a signature prepended to a message to form a+-- signed message.+sign_bytes          = fromIntegral c_crypto_sign_bytes+-- | The size of a public key for signing verification+sign_publickeybytes = fromIntegral c_crypto_sign_publickeybytes+-- | The size of a secret key for signing+sign_secretkeybytes = fromIntegral c_crypto_sign_secretkeybytes++-- src/libsodium/crypto_sign/crypto_sign.c+foreign import ccall "crypto_sign_bytes"+  c_crypto_sign_bytes :: CSize+foreign import ccall "crypto_sign_publickeybytes"+  c_crypto_sign_publickeybytes :: CSize+foreign import ccall "crypto_sign_secretkeybytes"+  c_crypto_sign_secretkeybytes :: CSize+++foreign import ccall "crypto_sign_keypair"+  c_sign_keypair :: Ptr CChar+                 -- ^ Public key output buffer+                 -> Ptr CChar+                 -- ^ Secret key output buffer+                 -> IO CInt+                 -- ^ Always 0++foreign import ccall "crypto_sign"+  c_sign :: Ptr CChar+         -- ^ Signed message output buffer+         -> Ptr CULLong+         -- ^ Length of signed message+         -> Ptr CChar+         -- ^ Constant message buffer+         -> CULLong+         -- ^ Length of message input buffer+         -> Ptr CChar+         -- ^ Constant secret key buffer+         -> IO CInt+         -- ^ Always 0++foreign import ccall "crypto_sign_open"+  c_sign_open :: Ptr CChar+              -- ^ Message output buffer+              -> Ptr CULLong+              -- ^ Length of message+              -> Ptr CChar+              -- ^ Constant signed message buffer+              -> CULLong+              -- ^ Length of signed message buffer+              -> Ptr CChar+              -- ^ Public key buffer+              -> IO CInt+              -- ^ 0 if signature is verifiable, -1 otherwise++foreign import ccall "crypto_sign_detached"+    c_sign_detached :: Ptr CChar+                    -- ^ Signature output buffer+                    -> Ptr CULLong+                    -- ^ Length of the signature+                    -> Ptr CChar+                    -- ^ Constant message buffer+                    -> CULLong+                    -- ^ Length of message buffer+                    -> Ptr CChar+                    -- ^ Constant secret key buffer+                    -> IO CInt+foreign import ccall "crypto_sign_verify_detached"+    c_sign_verify_detached :: Ptr CChar+                           -- ^ Signature buffer+                           -> Ptr CChar+                           -- ^ Constant signed message buffer+                           -> CULLong+                           -- ^ Length of signed message buffer+                           -> Ptr CChar+                           -- ^ Public key buffer+                           -> IO CInt++foreign import ccall "crypto_sign_ed25519_pk_to_curve25519"+    c_sign_ed25519_pk_to_curve25519 :: Ptr CChar+                                    -- ^ X25519 public key output buffer+                                    -> Ptr CChar+                                    -- ^ Ed25519 public key buffer+                                    -> IO CInt++foreign import ccall "crypto_sign_ed25519_sk_to_curve25519"+    c_sign_ed25519_sk_to_curve25519 :: Ptr CChar+                                    -- ^ X25519 secret key output buffer+                                    -> Ptr CChar+                                    -- ^ Ed25519 secret key buffer+                                    -> IO CInt
+ src/Crypto/Saltine/Internal/Stream.hs view
@@ -0,0 +1,107 @@+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving, DeriveGeneric, ForeignFunctionInterface #-}+-- |+-- Module      : Crypto.Saltine.Internal.Stream+-- Copyright   : (c) Max Amanshauser 2021+-- License     : MIT+--+-- Maintainer  : max@lambdalifting.org+-- Stability   : experimental+-- Portability : non-portable+--+module Crypto.Saltine.Internal.Stream (+    stream_keybytes+  , stream_noncebytes+  , c_stream+  , c_stream_xor+  , Key(..)+  , Nonce(..)+) where++import Control.DeepSeq              (NFData)+import Crypto.Saltine.Class+import Crypto.Saltine.Core.Hash     (shorthash)+import Crypto.Saltine.Internal.Hash (nullShKey)+import Crypto.Saltine.Internal.Util as U+import Data.ByteString              (ByteString)+import Data.Data                    (Data, Typeable)+import Data.Hashable                (Hashable)+import Data.Monoid+import Foreign.C+import Foreign.Ptr+import GHC.Generics                 (Generic)++import qualified Data.ByteString as S++-- | An opaque 'stream' cryptographic key.+newtype Key = Key { unKey :: ByteString } deriving (Ord, Hashable, Data, Typeable, Generic, NFData)+instance Eq Key where+    Key a == Key b = U.compare a b+instance Show Key where+    show k = "Stream.Key {hashesTo = \"" <> (bin2hex . shorthash nullShKey $ encode k) <> "\"}"++instance IsEncoding Key where+  decode v = if S.length v == stream_keybytes+           then Just (Key v)+           else Nothing+  {-# INLINE decode #-}+  encode (Key v) = v+  {-# INLINE encode #-}++-- | An opaque 'stream' nonce.+newtype Nonce = Nonce { unNonce :: ByteString } deriving (Eq, Ord, Hashable, Data, Typeable, Generic, NFData)+instance Show Nonce where+    show k = "Stream.Nonce " <> bin2hex (encode k)++instance IsNonce Nonce where+  zero = Nonce (S.replicate stream_noncebytes 0)+  nudge (Nonce n) = Nonce (nudgeBS n)++instance IsEncoding Nonce where+  decode v = if S.length v == stream_noncebytes+           then Just (Nonce v)+           else Nothing+  {-# INLINE decode #-}+  encode (Nonce v) = v+  {-# INLINE encode #-}++stream_keybytes, stream_noncebytes :: Int++-- Streams+-- | The size of a key for the cryptographic stream generation+stream_keybytes   = fromIntegral c_crypto_stream_keybytes+-- | The size of a nonce for the cryptographic stream generation+stream_noncebytes = fromIntegral c_crypto_stream_noncebytes++-- src/libsodium/crypto_stream/crypto_stream.c+-- src/libsodium/include/sodium/crypto_stream.h+foreign import ccall "crypto_stream_keybytes"+  c_crypto_stream_keybytes :: CSize+foreign import ccall "crypto_stream_noncebytes"+  c_crypto_stream_noncebytes :: CSize+++foreign import ccall "crypto_stream"+  c_stream :: Ptr CChar+           -- ^ Stream output buffer+           -> CULLong+           -- ^ Length of stream to generate+           -> Ptr CChar+           -- ^ Constant nonce buffer+           -> Ptr CChar+           -- ^ Constant key buffer+           -> IO CInt+           -- ^ Always 0++foreign import ccall "crypto_stream_xor"+  c_stream_xor :: Ptr CChar+               -- ^ Ciphertext output buffer+               -> Ptr CChar+               -- ^ Constant message buffer+               -> CULLong+               -- ^ Length of message buffer+               -> Ptr CChar+               -- ^ Constant nonce buffer+               -> Ptr CChar+               -- ^ Constant key buffer+               -> IO CInt+               -- ^ Always 0
src/Crypto/Saltine/Internal/Util.hs view
@@ -1,16 +1,29 @@-module Crypto.Saltine.Internal.Util where+{-# LANGUAGE BangPatterns #-} -import           Foreign.C-import           Foreign.Marshal.Alloc    (mallocBytes)-import           Foreign.Ptr-import           System.IO.Unsafe+module Crypto.Saltine.Internal.Util (+    module Crypto.Saltine.Internal.Util+  , withCString+  , allocaBytes+)+where -import           Control.Applicative-import qualified Data.ByteString        as S-import           Data.ByteString          (ByteString)-import           Data.ByteString.Unsafe-import           Data.Monoid+import Data.ByteString          (ByteString)+import Data.ByteString.Unsafe+import Data.Monoid+import Foreign.C+import Foreign.Marshal.Alloc    (mallocBytes, allocaBytes)+import Foreign.Ptr+import GHC.Word                 (Word8)+import System.IO.Unsafe +import qualified Data.ByteString       as S+import qualified Data.ByteString.Char8 as S8++-- | Returns @Nothing@ if the subtraction would result in an+-- underflow or a negative number.+safeSubtract :: (Ord a, Num a) => a -> a -> Maybe a+x `safeSubtract` y = if y > x then Nothing else Just (x - y)+ -- | @snd . cycleSucc@ computes the 'succ' of a 'Bounded', 'Eq' 'Enum' -- with wraparound. The @fst . cycleSuc@ is whether the wraparound -- occurred (i.e. @fst . cycleSucc == (== maxBound)@).@@ -58,6 +71,12 @@   where go 0 = True         go _ = False +withCStrings :: [String] -> ([CString] -> IO a) -> IO a+withCStrings = foldr (\v kk -> \k -> (withCString v) (\a -> kk (\as -> k (a:as)))) ($ [])++withCStringLens :: [String] -> ([CStringLen] -> IO a) -> IO a+withCStringLens = foldr (\v kk -> \k -> (withCStringLen v) (\a -> kk (\as -> k (a:as)))) ($ [])+ -- | Convenience function for accessing constant C strings constByteStrings :: [ByteString] -> ([CStringLen] -> IO b) -> IO b constByteStrings =@@ -72,6 +91,18 @@   out <- unsafeUseAsCString bs k   return (out, bs) ++-- | Sometimes we have to deal with variable-length strings+buildUnsafeVariableByteString' :: Int -> (Ptr CChar -> IO b) -> IO (b, ByteString)+buildUnsafeVariableByteString' n k = do+  ph  <- mallocBytes n+  out <- k ph+  bs  <- unsafePackMallocCString ph+  return (out, bs)++buildUnsafeVariableByteString :: Int -> (Ptr CChar -> IO b) -> (b, ByteString)+buildUnsafeVariableByteString n = unsafePerformIO . buildUnsafeVariableByteString' n+ -- | Extremely unsafe function, use with utmost care! Builds a new -- ByteString using a ccall which is given access to the raw underlying -- pointer. Overwrites are UNCHECKED and 'unsafePerformIO' is used so@@ -91,3 +122,67 @@  foreign import ccall "randombytes_buf"   c_randombytes_buf :: Ptr CChar -> CInt -> IO ()++-- | Constant time memory comparison+foreign import ccall unsafe "sodium_memcmp"+  c_sodium_memcmp+    :: Ptr CChar -- a+    -> Ptr CChar -- b+    -> CInt   -- Length+    -> IO CInt++foreign import ccall unsafe "sodium_malloc"+  c_sodium_malloc+    :: CSize -> IO (Ptr a)++foreign import ccall unsafe "sodium_free"+  c_sodium_free+    :: Ptr Word8 -> IO ()++-- | Not sure yet what to use this for+buildUnsafeScrubbedByteString' :: Int -> (Ptr CChar -> IO b) -> IO (b,ByteString)+buildUnsafeScrubbedByteString' n k = do+    p <- c_sodium_malloc (fromIntegral n)++    bs <- unsafePackCStringFinalizer p n (c_sodium_free p)+    out <- unsafeUseAsCString bs k+    pure (out,bs)++-- | Not sure yet what to use this for+buildUnsafeScrubbedByteString :: Int -> (Ptr CChar -> IO b) -> (b,ByteString)+buildUnsafeScrubbedByteString n = unsafePerformIO . buildUnsafeScrubbedByteString' n++-- | Constant-time comparison+compare :: ByteString -> ByteString -> Bool+compare a b =+    (S.length a == S.length b) && unsafePerformIO (constByteStrings [a, b] $ \+        [(bsa, _), (bsb,_)] ->+            (== 0) <$> c_sodium_memcmp bsa bsb (fromIntegral $ S.length a))++-- | bin2hex conversion for showing various binary types+foreign import ccall unsafe "sodium_bin2hex"+  c_sodium_bin2hex+    :: Ptr CChar            -- Target zone+    -> CInt                 -- Max. length of target string (must be min. bin_len * 2 + 1)+    -> Ptr CChar            -- Source+    -> CInt                 -- Source length+    -> IO (Ptr CChar)++bin2hex :: ByteString -> String+bin2hex bs = let tlen = S.length bs * 2 + 1 in+    S8.unpack . S8.init . snd . buildUnsafeByteString tlen $ \t ->+        constByteStrings [bs] $ \+            [(pbs, _)] ->+                c_sodium_bin2hex t (fromIntegral tlen) pbs (fromIntegral $ S.length bs)++uncurry3 :: (a -> b -> c -> d) -> ((a, b, c) -> d)+uncurry3 f ~(a,b,c) = f a b c++uncurry5 :: (a -> b -> c -> d -> e -> f) -> ((a, b, c, d, e) -> f)+uncurry5 f ~(a,b,c,d,e) = f a b c d e++(!&&!) :: Bool -> Bool -> Bool+(!&&!) !a !b = a && b++(!||!) :: Bool -> Bool -> Bool+(!||!) !a !b = a || b
+ tests/AEAD/AES256GCMProperties.hs view
@@ -0,0 +1,125 @@+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module AEAD.AES256GCMProperties (+  testAEADAES+  ) where++import           Util+import           Crypto.Saltine.Core.AEAD.AES256GCM+import           Crypto.Saltine.Class (decode)+import           Crypto.Saltine.Internal.AEAD.AES256GCM as Bytes++import qualified Data.ByteString                      as S+import           Data.Maybe (fromJust)+import           Test.Framework.Providers.QuickCheck2+import           Test.Framework+import           Test.QuickCheck (Property, (==>))+import           Test.QuickCheck.Arbitrary++instance Arbitrary Nonce where+    arbitrary =+        do bs <- S.pack <$> vector Bytes.aead_aes256gcm_npubbytes+           pure $ fromJust (decode bs)++instance Arbitrary Key where+    arbitrary =+        do bs <- S.pack <$> vector Bytes.aead_aes256gcm_keybytes+           pure $ fromJust (decode bs)++-- | Ciphertext can be decrypted+rightInverseProp :: Key -> Nonce -> Message -> Message -> Bool+rightInverseProp k n (Message bs) (Message aad) =+  Just bs == aeadOpen k n (aead k n bs aad) aad++-- | Detached ciphertext/tag can be decrypted+rightInverseDetachedProp :: Key -> Nonce -> Message -> Message -> Bool+rightInverseDetachedProp k n (Message bs) (Message aad) =+  let (tag,ct) = aeadDetached k n bs aad+  in Just bs == aeadOpenDetached k n tag ct aad++-- | Ciphertext cannot be decrypted if the ciphertext is perturbed+rightInverseFailureProp :: Key -> Nonce -> Message -> Message -> Perturb -> Property+rightInverseFailureProp k n (Message bs) (Message aad) p =+  S.length bs /= 0 ==>+   let ct = aead k n bs aad+       fakeCT = perturb ct p+   in fakeCT /= ct ==> Nothing == aeadOpen k n fakeCT aad++-- | Ciphertext cannot be decrypted if the aad is perturbed+rightInverseAADFailureProp :: Key -> Nonce -> Message -> Message -> Message -> Property+rightInverseAADFailureProp k n (Message bs) (Message aad) (Message aad2) =+  aad /= aad2 ==> Nothing == aeadOpen k n (aead k n bs aad) aad2++-- | Ciphertext cannot be decrypted if the tag is perturbed+rightInverseTagFailureProp :: Key -> Nonce -> Message -> Message -> Message -> Property+rightInverseTagFailureProp k n (Message bs) (Message aad) (Message newTag) =+   let (tag,ct) = aeadDetached k n bs aad+   in newTag /= tag ==> Nothing == aeadOpenDetached k n newTag ct aad++-- | Ciphertext cannot be decrypted if the ciphertext is perturbed+rightInverseFailureDetachedProp :: Key -> Nonce -> Message -> Message -> Perturb -> Property+rightInverseFailureDetachedProp k n (Message bs) (Message aad) p@(Perturb pBytes) =+  let (tag,ct) = aeadDetached k n bs aad+  in S.length bs > length pBytes ==>+        Nothing == aeadOpenDetached k n tag (perturb ct p) aad++-- | Ciphertext cannot be decrypted with a different key+cannotDecryptKeyProp :: Key -> Key -> Nonce -> Message -> Message -> Property+cannotDecryptKeyProp k1 k2 n (Message bs) (Message aad) =+  let ct = aead k1 n bs aad+  in k1 /= k2 ==> Nothing == aeadOpen k2 n ct aad++-- | Ciphertext cannot be decrypted with a different key+cannotDecryptKeyDetachedProp :: Key -> Key -> Nonce -> Message -> Message -> Property+cannotDecryptKeyDetachedProp k1 k2 n (Message bs) (Message aad) =+  let (tag,ct) = aeadDetached k1 n bs aad+  in k1 /= k2 ==> Nothing == aeadOpenDetached k2 n tag ct aad++-- | Ciphertext cannot be decrypted with a different nonce+cannotDecryptNonceProp :: Key -> Nonce -> Nonce -> Message -> Message -> Property+cannotDecryptNonceProp k n1 n2 (Message bs) (Message aad) =+  n1 /= n2 ==> Nothing == aeadOpen k n2 (aead k n1 bs aad) aad++-- | Ciphertext cannot be decrypted with a different nonce+cannotDecryptNonceDetachedProp :: Key -> Nonce -> Nonce -> Message -> Message -> Property+cannotDecryptNonceDetachedProp k n1 n2 (Message bs) (Message aad) =+  let (tag,ct) = aeadDetached k n1 bs aad+  in n1 /= n2 ==> Nothing == aeadOpenDetached k n2 tag ct aad++testAEADAES :: Test+testAEADAES = buildTest $++  return $ testGroup "...Internal.AEAD.AES256GCM" $ if not aead_aes256gcm_available then [] else [+    testProperty "Can decrypt ciphertext" rightInverseProp,++    testProperty "Can decrypt ciphertext (detached)" rightInverseDetachedProp,++    testGroup "Cannot decrypt ciphertext when..." [++      testProperty "... ciphertext is perturbed"+      $ rightInverseFailureProp,++      testProperty "... AAD is perturbed"+      $ rightInverseAADFailureProp,++      testProperty "... ciphertext is perturbed (detached)"+      $ rightInverseFailureDetachedProp,++      testProperty "... tag is perturbed (detached)"+      $ rightInverseTagFailureProp,++      testProperty "... using the wrong key"+      $ cannotDecryptKeyProp,++      testProperty "... using the wrong key (detached)"+      $ cannotDecryptKeyDetachedProp,++      testProperty "... using the wrong nonce"+      $ cannotDecryptNonceProp,++      testProperty "... using the wrong nonce (detached"+      $ cannotDecryptNonceDetachedProp++      ]+    ]
+ tests/AEAD/ChaCha20Poly1305IETFProperties.hs view
@@ -0,0 +1,128 @@+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module AEAD.ChaCha20Poly1305IETFProperties (+  testAEADIETF+  ) where++import           Util+import           Crypto.Saltine.Core.AEAD.ChaCha20Poly1305IETF+import           Crypto.Saltine.Class (decode)+import           Crypto.Saltine.Internal.AEAD.ChaCha20Poly1305IETF as Bytes++import qualified Data.ByteString                      as S+import           Data.Maybe (fromJust)+import           Test.Framework.Providers.QuickCheck2+import           Test.Framework+import           Test.QuickCheck (Property, (==>))+import           Test.QuickCheck.Arbitrary++instance Arbitrary Nonce where+    arbitrary =+        do bs <- S.pack <$> vector Bytes.aead_chacha20poly1305_ietf_npubbytes+           pure $ fromJust (decode bs)++instance Arbitrary Key where+    arbitrary =+        do bs <- S.pack <$> vector Bytes.aead_chacha20poly1305_ietf_keybytes+           pure $ fromJust (decode bs)++-- | Ciphertext can be decrypted+rightInverseProp :: Key -> Nonce -> Message -> Message -> Bool+rightInverseProp k n (Message bs) (Message aad) =+  Just bs == aeadOpen k n (aead k n bs aad) aad++-- | Detached ciphertext/tag can be decrypted+rightInverseDetachedProp :: Key -> Nonce -> Message -> Message -> Bool+rightInverseDetachedProp k n (Message bs) (Message aad) =+  let (tag,ct) = aeadDetached k n bs aad+  in Just bs == aeadOpenDetached k n tag ct aad++-- | Ciphertext cannot be decrypted if the ciphertext is perturbed+rightInverseFailureProp :: Key -> Nonce -> Message -> Message -> Perturb -> Property+rightInverseFailureProp k n (Message bs) (Message aad) p =+  S.length bs /= 0 ==>+   let ct = aead k n bs aad+       fakeCT = perturb ct p+   in fakeCT /= ct ==> Nothing == aeadOpen k n fakeCT aad++-- | Ciphertext cannot be decrypted if the aad is perturbed+rightInverseAADFailureProp :: Key -> Nonce -> Message -> Message -> Message -> Property+rightInverseAADFailureProp k n (Message bs) (Message aad) (Message aad2) =+  aad /= aad2 ==> Nothing == aeadOpen k n (aead k n bs aad) aad2++-- | Ciphertext cannot be decrypted if the tag is perturbed+rightInverseTagFailureProp :: Key -> Nonce -> Message -> Message -> Message -> Property+rightInverseTagFailureProp k n (Message bs) (Message aad) (Message newTag) =+   let (tag,ct) = aeadDetached k n bs aad+   in newTag /= tag ==> Nothing == aeadOpenDetached k n newTag ct aad++-- | Ciphertext cannot be decrypted if the ciphertext is perturbed+rightInverseFailureDetachedProp :: Key -> Nonce -> Message -> Message -> Perturb -> Property+rightInverseFailureDetachedProp k n (Message bs) (Message aad) p@(Perturb pBytes) =+  let (tag,ct) = aeadDetached k n bs aad+  in S.length bs > length pBytes ==>+        Nothing == aeadOpenDetached k n tag (perturb ct p) aad++-- | Ciphertext cannot be decrypted with a different key+cannotDecryptKeyProp :: Key -> Key -> Nonce -> Message -> Message -> Property+cannotDecryptKeyProp k1 k2 n (Message bs) (Message aad) =+  let ct = aead k1 n bs aad+  in k1 /= k2 ==> Nothing == aeadOpen k2 n ct aad++-- | Ciphertext cannot be decrypted with a different key+cannotDecryptKeyDetachedProp :: Key -> Key -> Nonce -> Message -> Message -> Property+cannotDecryptKeyDetachedProp k1 k2 n (Message bs) (Message aad) =+  let (tag,ct) = aeadDetached k1 n bs aad+  in k1 /= k2 ==> Nothing == aeadOpenDetached k2 n tag ct aad++-- | Ciphertext cannot be decrypted with a different nonce+cannotDecryptNonceProp :: Key -> Nonce -> Nonce -> Message -> Message -> Property+cannotDecryptNonceProp k n1 n2 (Message bs) (Message aad) =+  n1 /= n2 ==> Nothing == aeadOpen k n2 (aead k n1 bs aad) aad++-- | Ciphertext cannot be decrypted with a different nonce+cannotDecryptNonceDetachedProp :: Key -> Nonce -> Nonce -> Message -> Message -> Property+cannotDecryptNonceDetachedProp k n1 n2 (Message bs) (Message aad) =+  let (tag,ct) = aeadDetached k n1 bs aad+  in n1 /= n2 ==> Nothing == aeadOpenDetached k n2 tag ct aad++testAEADIETF :: Test+testAEADIETF = buildTest $ do++  return $ testGroup "...Internal.AEAD.ChaCha20Poly1305IETF" [++    testProperty "Can decrypt ciphertext"+    $ rightInverseProp,++    testProperty "Can decrypt ciphertext (detached)"+    $ rightInverseDetachedProp,++    testGroup "Cannot decrypt ciphertext when..." [++      testProperty "... ciphertext is perturbed"+      $ rightInverseFailureProp,++      testProperty "... AAD is perturbed"+      $ rightInverseAADFailureProp,++      testProperty "... ciphertext is perturbed (detached)"+      $ rightInverseFailureDetachedProp,++      testProperty "... tag is perturbed (detached)"+      $ rightInverseTagFailureProp,++      testProperty "... using the wrong key"+      $ cannotDecryptKeyProp,++      testProperty "... using the wrong key (detached)"+      $ cannotDecryptKeyDetachedProp,++      testProperty "... using the wrong nonce"+      $ cannotDecryptNonceProp,++      testProperty "... using the wrong nonce (detached"+      $ cannotDecryptNonceDetachedProp++      ]+    ]
+ tests/AEAD/ChaCha20Poly1305Properties.hs view
@@ -0,0 +1,128 @@+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module AEAD.ChaCha20Poly1305Properties (+  testAEADChaCha20+  ) where++import           Util+import           Crypto.Saltine.Core.AEAD.ChaCha20Poly1305IETF+import           Crypto.Saltine.Class (decode)+import           Crypto.Saltine.Internal.AEAD.ChaCha20Poly1305IETF as Bytes++import qualified Data.ByteString                      as S+import           Data.Maybe (fromJust)+import           Test.Framework.Providers.QuickCheck2+import           Test.Framework+import           Test.QuickCheck (Property, (==>))+import           Test.QuickCheck.Arbitrary++instance Arbitrary Nonce where+    arbitrary =+        do bs <- S.pack <$> vector Bytes.aead_chacha20poly1305_ietf_npubbytes+           pure $ fromJust (decode bs)++instance Arbitrary Key where+    arbitrary =+        do bs <- S.pack <$> vector Bytes.aead_chacha20poly1305_ietf_keybytes+           pure $ fromJust (decode bs)++-- | Ciphertext can be decrypted+rightInverseProp :: Key -> Nonce -> Message -> Message -> Bool+rightInverseProp k n (Message bs) (Message aad) =+  Just bs == aeadOpen k n (aead k n bs aad) aad++-- | Detached ciphertext/tag can be decrypted+rightInverseDetachedProp :: Key -> Nonce -> Message -> Message -> Bool+rightInverseDetachedProp k n (Message bs) (Message aad) =+  let (tag,ct) = aeadDetached k n bs aad+  in Just bs == aeadOpenDetached k n tag ct aad++-- | Ciphertext cannot be decrypted if the ciphertext is perturbed+rightInverseFailureProp :: Key -> Nonce -> Message -> Message -> Perturb -> Property+rightInverseFailureProp k n (Message bs) (Message aad) p =+  S.length bs /= 0 ==>+   let ct = aead k n bs aad+       fakeCT = perturb ct p+   in fakeCT /= ct ==> Nothing == aeadOpen k n fakeCT aad++-- | Ciphertext cannot be decrypted if the aad is perturbed+rightInverseAADFailureProp :: Key -> Nonce -> Message -> Message -> Message -> Property+rightInverseAADFailureProp k n (Message bs) (Message aad) (Message aad2) =+  aad /= aad2 ==> Nothing == aeadOpen k n (aead k n bs aad) aad2++-- | Ciphertext cannot be decrypted if the tag is perturbed+rightInverseTagFailureProp :: Key -> Nonce -> Message -> Message -> Message -> Property+rightInverseTagFailureProp k n (Message bs) (Message aad) (Message newTag) =+   let (tag,ct) = aeadDetached k n bs aad+   in newTag /= tag ==> Nothing == aeadOpenDetached k n newTag ct aad++-- | Ciphertext cannot be decrypted if the ciphertext is perturbed+rightInverseFailureDetachedProp :: Key -> Nonce -> Message -> Message -> Perturb -> Property+rightInverseFailureDetachedProp k n (Message bs) (Message aad) p@(Perturb pBytes) =+  let (tag,ct) = aeadDetached k n bs aad+  in S.length bs > length pBytes ==>+        Nothing == aeadOpenDetached k n tag (perturb ct p) aad++-- | Ciphertext cannot be decrypted with a different key+cannotDecryptKeyProp :: Key -> Key -> Nonce -> Message -> Message -> Property+cannotDecryptKeyProp k1 k2 n (Message bs) (Message aad) =+  let ct = aead k1 n bs aad+  in k1 /= k2 ==> Nothing == aeadOpen k2 n ct aad++-- | Ciphertext cannot be decrypted with a different key+cannotDecryptKeyDetachedProp :: Key -> Key -> Nonce -> Message -> Message -> Property+cannotDecryptKeyDetachedProp k1 k2 n (Message bs) (Message aad) =+  let (tag,ct) = aeadDetached k1 n bs aad+  in k1 /= k2 ==> Nothing == aeadOpenDetached k2 n tag ct aad++-- | Ciphertext cannot be decrypted with a different nonce+cannotDecryptNonceProp :: Key -> Nonce -> Nonce -> Message -> Message -> Property+cannotDecryptNonceProp k n1 n2 (Message bs) (Message aad) =+  n1 /= n2 ==> Nothing == aeadOpen k n2 (aead k n1 bs aad) aad++-- | Ciphertext cannot be decrypted with a different nonce+cannotDecryptNonceDetachedProp :: Key -> Nonce -> Nonce -> Message -> Message -> Property+cannotDecryptNonceDetachedProp k n1 n2 (Message bs) (Message aad) =+  let (tag,ct) = aeadDetached k n1 bs aad+  in n1 /= n2 ==> Nothing == aeadOpenDetached k n2 tag ct aad++testAEADChaCha20 :: Test+testAEADChaCha20 = buildTest $ do++  return $ testGroup "...Internal.AEAD.ChaCha20Poly1305" [++    testProperty "Can decrypt ciphertext"+    $ rightInverseProp,++    testProperty "Can decrypt ciphertext (detached)"+    $ rightInverseDetachedProp,++    testGroup "Cannot decrypt ciphertext when..." [++      testProperty "... ciphertext is perturbed"+      $ rightInverseFailureProp,++      testProperty "... AAD is perturbed"+      $ rightInverseAADFailureProp,++      testProperty "... ciphertext is perturbed (detached)"+      $ rightInverseFailureDetachedProp,++      testProperty "... tag is perturbed (detached)"+      $ rightInverseTagFailureProp,++      testProperty "... using the wrong key"+      $ cannotDecryptKeyProp,++      testProperty "... using the wrong key (detached)"+      $ cannotDecryptKeyDetachedProp,++      testProperty "... using the wrong nonce"+      $ cannotDecryptNonceProp,++      testProperty "... using the wrong nonce (detached"+      $ cannotDecryptNonceDetachedProp++      ]+    ]
+ tests/AEAD/XChaCha20Poly1305Properties.hs view
@@ -0,0 +1,128 @@+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module AEAD.XChaCha20Poly1305Properties (+  testAEADXChaCha20+  ) where++import           Util+import           Crypto.Saltine.Core.AEAD.XChaCha20Poly1305+import           Crypto.Saltine.Class (decode)+import           Crypto.Saltine.Internal.AEAD.XChaCha20Poly1305 as Bytes++import qualified Data.ByteString                      as S+import           Data.Maybe (fromJust)+import           Test.Framework.Providers.QuickCheck2+import           Test.Framework+import           Test.QuickCheck (Property, (==>))+import           Test.QuickCheck.Arbitrary++instance Arbitrary Nonce where+    arbitrary =+        do bs <- S.pack <$> vector Bytes.aead_xchacha20poly1305_ietf_npubbytes+           pure $ fromJust (decode bs)++instance Arbitrary Key where+    arbitrary =+        do bs <- S.pack <$> vector Bytes.aead_xchacha20poly1305_ietf_keybytes+           pure $ fromJust (decode bs)++-- | Ciphertext can be decrypted+rightInverseProp :: Key -> Nonce -> Message -> Message -> Bool+rightInverseProp k n (Message bs) (Message aad) =+  Just bs == aeadOpen k n (aead k n bs aad) aad++-- | Detached ciphertext/tag can be decrypted+rightInverseDetachedProp :: Key -> Nonce -> Message -> Message -> Bool+rightInverseDetachedProp k n (Message bs) (Message aad) =+  let (tag,ct) = aeadDetached k n bs aad+  in Just bs == aeadOpenDetached k n tag ct aad++-- | Ciphertext cannot be decrypted if the ciphertext is perturbed+rightInverseFailureProp :: Key -> Nonce -> Message -> Message -> Perturb -> Property+rightInverseFailureProp k n (Message bs) (Message aad) p =+  S.length bs /= 0 ==>+   let ct = aead k n bs aad+       fakeCT = perturb ct p+   in fakeCT /= ct ==> Nothing == aeadOpen k n fakeCT aad++-- | Ciphertext cannot be decrypted if the aad is perturbed+rightInverseAADFailureProp :: Key -> Nonce -> Message -> Message -> Message -> Property+rightInverseAADFailureProp k n (Message bs) (Message aad) (Message aad2) =+  aad /= aad2 ==> Nothing == aeadOpen k n (aead k n bs aad) aad2++-- | Ciphertext cannot be decrypted if the tag is perturbed+rightInverseTagFailureProp :: Key -> Nonce -> Message -> Message -> Message -> Property+rightInverseTagFailureProp k n (Message bs) (Message aad) (Message newTag) =+   let (tag,ct) = aeadDetached k n bs aad+   in newTag /= tag ==> Nothing == aeadOpenDetached k n newTag ct aad++-- | Ciphertext cannot be decrypted if the ciphertext is perturbed+rightInverseFailureDetachedProp :: Key -> Nonce -> Message -> Message -> Perturb -> Property+rightInverseFailureDetachedProp k n (Message bs) (Message aad) p@(Perturb pBytes) =+  let (tag,ct) = aeadDetached k n bs aad+  in S.length bs > length pBytes ==>+        Nothing == aeadOpenDetached k n tag (perturb ct p) aad++-- | Ciphertext cannot be decrypted with a different key+cannotDecryptKeyProp :: Key -> Key -> Nonce -> Message -> Message -> Property+cannotDecryptKeyProp k1 k2 n (Message bs) (Message aad) =+  let ct = aead k1 n bs aad+  in k1 /= k2 ==> Nothing == aeadOpen k2 n ct aad++-- | Ciphertext cannot be decrypted with a different key+cannotDecryptKeyDetachedProp :: Key -> Key -> Nonce -> Message -> Message -> Property+cannotDecryptKeyDetachedProp k1 k2 n (Message bs) (Message aad) =+  let (tag,ct) = aeadDetached k1 n bs aad+  in k1 /= k2 ==> Nothing == aeadOpenDetached k2 n tag ct aad++-- | Ciphertext cannot be decrypted with a different nonce+cannotDecryptNonceProp :: Key -> Nonce -> Nonce -> Message -> Message -> Property+cannotDecryptNonceProp k n1 n2 (Message bs) (Message aad) =+  n1 /= n2 ==> Nothing == aeadOpen k n2 (aead k n1 bs aad) aad++-- | Ciphertext cannot be decrypted with a different nonce+cannotDecryptNonceDetachedProp :: Key -> Nonce -> Nonce -> Message -> Message -> Property+cannotDecryptNonceDetachedProp k n1 n2 (Message bs) (Message aad) =+  let (tag,ct) = aeadDetached k n1 bs aad+  in n1 /= n2 ==> Nothing == aeadOpenDetached k n2 tag ct aad++testAEADXChaCha20 :: Test+testAEADXChaCha20 = buildTest $ do++  return $ testGroup "...Internal.AEAD.XChaCha20Poly1305" [++    testProperty "Can decrypt ciphertext"+    $ rightInverseProp,++    testProperty "Can decrypt ciphertext (detached)"+    $ rightInverseDetachedProp,++    testGroup "Cannot decrypt ciphertext when..." [++      testProperty "... ciphertext is perturbed"+      $ rightInverseFailureProp,++      testProperty "... AAD is perturbed"+      $ rightInverseAADFailureProp,++      testProperty "... ciphertext is perturbed (detached)"+      $ rightInverseFailureDetachedProp,++      testProperty "... tag is perturbed (detached)"+      $ rightInverseTagFailureProp,++      testProperty "... using the wrong key"+      $ cannotDecryptKeyProp,++      testProperty "... using the wrong key (detached)"+      $ cannotDecryptKeyDetachedProp,++      testProperty "... using the wrong nonce"+      $ cannotDecryptNonceProp,++      testProperty "... using the wrong nonce (detached"+      $ cannotDecryptNonceDetachedProp++      ]+    ]
− tests/AEADProperties.hs
@@ -1,132 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module AEADProperties (-  testAEAD-  ) where--import           Util-import           Crypto.Saltine.Core.AEAD-import           Crypto.Saltine.Class (decode,encode)-import           Crypto.Saltine.Internal.ByteSizes as Bytes--import qualified Data.ByteString                      as S-import           Test.Framework.Providers.QuickCheck2-import           Test.Framework-import           Test.QuickCheck (Property, (==>))-import           Test.QuickCheck.Arbitrary--instance Arbitrary Nonce where-    arbitrary =-        do bs <- S.pack <$> vector Bytes.secretBoxNonce-           maybe (fail "impossible arbitrary failure.") pure (decode bs)--instance Arbitrary Key where-    arbitrary =-        do bs <- S.pack <$> vector Bytes.secretBoxKey-           maybe (fail "impossible arbitrary failure.") pure (decode bs)--instance Show Key where-    show = show . encode-instance Show Nonce where-    show = show . encode---- | Ciphertext can be decrypted-rightInverseProp :: Key -> Nonce -> Message -> Message -> Bool-rightInverseProp k n (Message bs) (Message aad) =-  Just bs == aeadOpen k n (aead k n bs aad) aad---- | Detached ciphertext/tag can be decrypted-rightInverseDetachedProp :: Key -> Nonce -> Message -> Message -> Bool-rightInverseDetachedProp k n (Message bs) (Message aad) =-  let (tag,ct) = aeadDetached k n bs aad-  in Just bs == aeadOpenDetached k n tag ct aad---- | Ciphertext cannot be decrypted if the ciphertext is perturbed-rightInverseFailureProp :: Key -> Nonce -> Message -> Message -> Perturb -> Property-rightInverseFailureProp k n (Message bs) (Message aad) p =-  S.length bs /= 0 ==>-   let ct = aead k n bs aad-       fakeCT = perturb ct p-   in fakeCT /= ct ==> Nothing == aeadOpen k n fakeCT aad---- | Ciphertext cannot be decrypted if the aad is perturbed-rightInverseAADFailureProp :: Key -> Nonce -> Message -> Message -> Message -> Property-rightInverseAADFailureProp k n (Message bs) (Message aad) (Message aad2) =-  aad /= aad2 ==> Nothing == aeadOpen k n (aead k n bs aad) aad2---- | Ciphertext cannot be decrypted if the tag is perturbed-rightInverseTagFailureProp :: Key -> Nonce -> Message -> Message -> Message -> Property-rightInverseTagFailureProp k n (Message bs) (Message aad) (Message newTag) =-   let (tag,ct) = aeadDetached k n bs aad-   in newTag /= tag ==> Nothing == aeadOpenDetached k n newTag ct aad---- | Ciphertext cannot be decrypted if the ciphertext is perturbed-rightInverseFailureDetachedProp :: Key -> Nonce -> Message -> Message -> Perturb -> Property-rightInverseFailureDetachedProp k n (Message bs) (Message aad) p@(Perturb pBytes) =-  let (tag,ct) = aeadDetached k n bs aad-  in S.length bs > length pBytes ==>-        Nothing == aeadOpenDetached k n tag (perturb ct p) aad---- | Ciphertext cannot be decrypted with a different key-cannotDecryptKeyProp :: Key -> Key -> Nonce -> Message -> Message -> Property-cannotDecryptKeyProp k1 k2 n (Message bs) (Message aad) =-  let ct = aead k1 n bs aad-  in k1 /= k2 ==> Nothing == aeadOpen k2 n ct aad---- | Ciphertext cannot be decrypted with a different key-cannotDecryptKeyDetachedProp :: Key -> Key -> Nonce -> Message -> Message -> Property-cannotDecryptKeyDetachedProp k1 k2 n (Message bs) (Message aad) =-  let (tag,ct) = aeadDetached k1 n bs aad-  in k1 /= k2 ==> Nothing == aeadOpenDetached k2 n tag ct aad---- | Ciphertext cannot be decrypted with a different nonce-cannotDecryptNonceProp :: Key -> Nonce -> Nonce -> Message -> Message -> Property-cannotDecryptNonceProp k n1 n2 (Message bs) (Message aad) =-  n1 /= n2 ==> Nothing == aeadOpen k n2 (aead k n1 bs aad) aad---- | Ciphertext cannot be decrypted with a different nonce-cannotDecryptNonceDetachedProp :: Key -> Nonce -> Nonce -> Message -> Message -> Property-cannotDecryptNonceDetachedProp k n1 n2 (Message bs) (Message aad) =-  let (tag,ct) = aeadDetached k n1 bs aad-  in n1 /= n2 ==> Nothing == aeadOpenDetached k n2 tag ct aad--testAEAD :: Test-testAEAD = buildTest $ do--  return $ testGroup "...Internal.AEAD" [--    testProperty "Can decrypt ciphertext"-    $ rightInverseProp,--    testProperty "Can decrypt ciphertext (detached)"-    $ rightInverseDetachedProp,--    testGroup "Cannot decrypt ciphertext when..." [--      testProperty "... ciphertext is perturbed"-      $ rightInverseFailureProp,--      testProperty "... AAD is perturbed"-      $ rightInverseAADFailureProp,--      testProperty "... ciphertext is perturbed (detached)"-      $ rightInverseFailureDetachedProp,--      testProperty "... tag is perturbed (detached)"-      $ rightInverseTagFailureProp,--      testProperty "... using the wrong key"-      $ cannotDecryptKeyProp,--      testProperty "... using the wrong key (detached)"-      $ cannotDecryptKeyDetachedProp,--      testProperty "... using the wrong nonce"-      $ cannotDecryptNonceProp,--      testProperty "... using the wrong nonce (detached"-      $ cannotDecryptNonceDetachedProp--      ]-    ]
tests/BoxProperties.hs view
@@ -5,49 +5,47 @@   testBox   ) where -import           Util-import           Crypto.Saltine.Core.Box-import qualified Data.ByteString                      as S-import           Data.Monoid--import           Test.Framework.Providers.QuickCheck2-import           Test.Framework-import           Test.QuickCheck.Property-import           Test.QuickCheck.Monadic+import Crypto.Saltine.Core.Box+import Data.Monoid+import Test.Framework.Providers.QuickCheck2+import Test.Framework+import Test.QuickCheck.Property+import Test.QuickCheck.Monadic+import Util  -- | Ciphertext can be decrypted rightInverseProp :: Keypair -> Keypair -> Nonce -> Message -> Bool-rightInverseProp (sk1, pk1) (sk2, pk2) n (Message bs) =+rightInverseProp (Keypair sk1 pk1) (Keypair 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 -> Perturb -> Bool-rightInverseFailureProp1 (sk1, pk1) (sk2, pk2) n (Message bs) p =+rightInverseFailureProp1 (Keypair sk1 pk1) (Keypair sk2 pk2) n (Message bs) p =   Nothing == boxOpen pk1 (perturb sk2 ([0] <> p)) n (box pk2 sk1 n bs)  -- | Cannot decrypt when not sent to you rightInverseFailureProp2 :: Keypair -> Keypair -> Nonce -> Message -> Perturb -> Bool-rightInverseFailureProp2 (sk1, pk1) (sk2, pk2) n (Message bs) p =+rightInverseFailureProp2 (Keypair sk1 pk1) (Keypair sk2 pk2) n (Message bs) p =   Nothing == boxOpen pk1 sk2 n (box (perturb pk2 p) sk1 n bs)  -- | Ciphertext cannot be decrypted (verification failure) if the -- ciphertext is perturbed rightInverseFailureProp3 :: Keypair -> Keypair -> Nonce -> Message -> Perturb -> Bool-rightInverseFailureProp3 (sk1, pk1) (sk2, pk2) n (Message bs) p =+rightInverseFailureProp3 (Keypair sk1 pk1) (Keypair sk2 pk2) n (Message bs) p =   Nothing == boxOpen pk1 sk2 n (perturb (box pk2 sk1 n bs) p)  -- | Ciphertext cannot be decrypted with a different nonce cannotDecryptNonceProp   :: Keypair -> Keypair -> Nonce -> Nonce -> Message -> Bool-cannotDecryptNonceProp (sk1, pk1) (sk2, pk2) n1 n2 (Message bs) =+cannotDecryptNonceProp (Keypair sk1 pk1) (Keypair 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+  Keypair sk1 pk1 <- newKeypair+  Keypair sk2 pk2 <- newKeypair   let ck_1for2 = beforeNM sk1 pk2       ck_2for1 = beforeNM sk2 pk1   return (ck_1for2 == ck_2for1)@@ -66,8 +64,8 @@  testBox :: Test testBox = buildTest $ do-  (sk1, pk1) <- newKeypair-  (sk2, pk2) <- newKeypair+  kp1@(Keypair sk1 pk1) <- newKeypair+  kp2@(Keypair sk2 pk2) <- newKeypair   let ck_1for2 = beforeNM sk1 pk2       ck_2for1 = beforeNM sk2 pk1   n1 <- newNonce@@ -78,7 +76,7 @@     testGroup "Can decrypt ciphertext using..." [         testProperty "... public key/secret key"-       $ rightInverseProp (sk1, pk1) (sk2, pk2) n1 ,+       $ rightInverseProp kp1 kp2 n1 ,         testProperty "... combined key"        $ rightInverseAfterNMProp ck_1for2 ck_2for1 n1@@ -88,16 +86,16 @@     testGroup "Fail to verify ciphertext when..." [        testProperty "... not using proper secret key"-      $ rightInverseFailureProp1 (sk1, pk1) (sk2, pk2) n1,+      $ rightInverseFailureProp1 kp1 kp2 n1,        testProperty "... not actually sent to you"-      $ rightInverseFailureProp2 (sk1, pk1) (sk2, pk2) n1,+      $ rightInverseFailureProp2 kp1 kp2 n1,        testProperty "... ciphertext has been perturbed"-      $ rightInverseFailureProp3 (sk1, pk1) (sk2, pk2) n1,+      $ rightInverseFailureProp3 kp1 kp2 n1,        testProperty "... using the wrong nonce"-      $ cannotDecryptNonceProp (sk1, pk1) (sk2, pk2) n1 n2,+      $ cannotDecryptNonceProp kp1 kp2 n1 n2,        testProperty "... using the wrong combined key"       $ rightInverseFailureAfterNMProp1 ck_1for2 ck_2for1 n1
+ tests/HashProperties.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE OverloadedStrings #-}++module HashProperties (+  testHash+  ) where++import           Util+import           Crypto.Saltine.Core.Hash++import qualified Data.ByteString                      as S+import           Test.Framework.Providers.QuickCheck2+import           Test.Framework+import           Test.QuickCheck++testHash :: Test+testHash = buildTest $ do+  shKey <- newShorthashKey+  shKey2 <- newShorthashKey+  ghKey <- newGenerichashKey 24 >>= maybe undefined return+  ghKey2 <- newGenerichashKey 24 >>= maybe undefined return+  let ghOutLen = maybe undefined id $ generichashOutLen 32++  return $ testGroup "...Internal.Hash" [++    testProperty "No two hashes are alike"+    $ \(Message bs1, Message bs2) -> bs1 /= bs2 ==> hash bs1 /= hash bs2,++    testProperty "Hash of empty ByteString is correct"+    $ \(Message bs) -> (bs == S.empty) ==> hash bs == (read hashEmptyBS :: S.ByteString),++    testProperty "SHA256 of empty ByteString is correct"+    $ \(Message bs) -> (bs == S.empty) ==> sha256 bs == (read sha256EmptyBS :: S.ByteString),++    testProperty "No two shorthashes are alike"+    $ \(Message bs1, Message bs2) -> bs1 /= bs2 ==> shorthash shKey bs1 /= shorthash shKey bs2,++    testProperty "Different keys produce different shorthashes"+    $ \(Message bs) -> shorthash shKey bs /= shorthash shKey2 bs,++    testProperty "No two generic hashes are alike"+    $ \(Message bs1, Message bs2) -> bs1 /= bs2 ==> generichash ghKey bs1 ghOutLen /= generichash ghKey bs2 ghOutLen,++    testProperty "No two SHA256 hashes are alike"+    $ \(Message bs1, Message bs2) -> bs1 /= bs2 ==> sha256 bs1 /= sha256 bs2,++    testProperty "Different keys produce different generichashes"+    $ \(Message bs) -> generichash ghKey bs ghOutLen /= generichash ghKey2 bs ghOutLen++    ]++  where+    hashEmptyBS   = "\"\207\131\225\&5~\239\184\189\241T(P\214m\128\a\214 \228\ENQ\vW\NAK\220\131\244\169!\211l\233\206G\208\209<]\133\242\176\255\131\CAN\210\135~\236/c\185\&1\189GAz\129\165\&82z\249'\218>\""+    sha256EmptyBS = "\"\227\176\196B\152\252\FS\DC4\154\251\244\200\153o\185$'\174A\228d\155\147L\164\149\153\ESCxR\184U\""
tests/Main.hs view
@@ -3,14 +3,20 @@ module Main where  import SecretBoxProperties   (testSecretBox)-import AEADProperties        (testAEAD)+import AEAD.ChaCha20Poly1305Properties      (testAEADChaCha20)+import AEAD.ChaCha20Poly1305IETFProperties  (testAEADIETF)+import AEAD.XChaCha20Poly1305Properties     (testAEADXChaCha20)+import AEAD.AES256GCMProperties             (testAEADAES) import BoxProperties         (testBox) import SealedBoxProperties   (testSealedBox) import StreamProperties      (testStream) import AuthProperties        (testAuth) import OneTimeAuthProperties (testOneTimeAuth) import SignProperties        (testSign)+import HashProperties        (testHash) import ScalarMultProperties  (testScalarMult)+import PasswordProperties    (testPassword)+import UtilProperties        (testUtils) import Crypto.Saltine  import Test.Framework@@ -27,13 +33,19 @@ main = do   sodiumInit   flip defaultMainWithOpts runOpts [+        testUtils,         testBox,         testSealedBox,         testSecretBox,-        testAEAD,+        testAEADChaCha20,+        testAEADIETF,+        testAEADXChaCha20,+        testAEADAES,         testStream,         testAuth,         testOneTimeAuth,         testSign,-        testScalarMult+        testHash,+        testScalarMult,+        testPassword         ]
+ tests/PasswordProperties.hs view
@@ -0,0 +1,97 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedLists #-}++module PasswordProperties (+  testPassword+) where++import Crypto.Saltine.Core.Password+import Crypto.Saltine.Internal.Util+import Data.Maybe                             (isJust, isNothing, fromJust)+import Data.Monoid+import Data.Text                              (Text)+import Test.Framework.Providers.QuickCheck2+import Test.Framework+import Test.QuickCheck++import qualified Crypto.Saltine.Internal.Password as I+import qualified Data.Text                        as T++instance Arbitrary Text where+    arbitrary = T.pack <$> arbitrary++instance Arbitrary Algorithm where+    arbitrary = elements $ enumFromTo minBound maxBound++-- | Sadly using the actual maximum limit is just way too slow+instance Arbitrary Memlimit where+    arbitrary = I.Memlimit <$> chooseInt ( max I.pwhash_argon2i_memlimit_min I.pwhash_argon2id_memlimit_min+                                         , max I.pwhash_argon2i_memlimit_min I.pwhash_argon2id_memlimit_min * 4+                                         )++instance Arbitrary Opslimit where+    arbitrary = I.Opslimit <$> chooseInt ( max I.pwhash_argon2i_opslimit_min I.pwhash_argon2id_opslimit_min+                                         , max I.pwhash_argon2i_opslimit_min I.pwhash_argon2id_opslimit_min * 4+                                         )++instance Arbitrary Policy where+    arbitrary = applyArbitrary3 Policy++rightInverseProp :: Text -> Policy -> IO Bool+rightInverseProp pw pol = do+    h <- pwhashStr pw pol+    pure $ pwhashStrVerify (fromJust h) pw++rightInverseFailureProp1 :: Text -> Policy -> Text -> IO Bool+rightInverseFailureProp1 pw pol per =+    let npw = T.reverse pw <> T.pack "0" <> per+    in do+        h <- pwhashStr pw pol+        pure . not $ pwhashStrVerify (fromJust h) npw++rightProp :: Text -> Policy -> IO Bool+rightProp pw pol = do+    h <- pwhashStr pw pol+    pure $ Just False == needsRehash (opsPolicy pol) (memPolicy pol) (fromJust h)++rightFailureProp :: Text -> Opslimit -> Opslimit -> Memlimit -> Memlimit -> IO Bool+rightFailureProp pw ops1 ops2 mem1 mem2 = do+    h <- pwhashStr pw (Policy ops1 mem1 defaultAlgorithm)+    pure $ Just True == needsRehash ops2 mem2 (fromJust h)+            || ops1 == ops2++rightFailureProp2 :: Text -> Opslimit -> Memlimit -> Bool+rightFailureProp2 invhash ops mem =+    isNothing $ needsRehash ops mem (I.PasswordHash invhash)++rightProp2 :: Salt -> Text -> Policy -> Gen Bool+rightProp2 salt pw pol = do+    i <- chooseInt (I.pwhash_bytes_min, 1024)++    pure $ isJust $ pwhash pw i salt pol+++testPassword :: Test+testPassword = buildTest $ do+  salt <- newSalt++  return $ testGroup "... Password" [++    testProperty "Can hash passwords and verify them..."+        $ ioProperty . uncurry rightInverseProp,++    testProperty "Hashed passwords cannot be verified with the wrong password..."+        $ ioProperty . uncurry3 rightInverseFailureProp1,++    testProperty "Hashed passwords do not need to be rehashed with the same policy..."+        $ ioProperty . uncurry rightProp,++    testProperty "Hashed passwords do need to be rehashed with a different policy..."+        $ ioProperty . uncurry5 rightFailureProp,++    testProperty "needsRehash detects invalid hashes..."+        rightFailureProp2,++    testProperty "Deriving a key from a password..."+        (rightProp2 salt)+    ]
tests/SealedBoxProperties.hs view
@@ -5,69 +5,67 @@   testSealedBox ) where -import           Util-import           Crypto.Saltine.Core.Box-import           Data.Monoid--import qualified Data.ByteString                      as S-import           Test.Framework.Providers.QuickCheck2-import           Test.Framework-import           Test.QuickCheck.Property               (ioProperty)+import Crypto.Saltine.Core.Box+import Data.Monoid+import Test.Framework.Providers.QuickCheck2+import Test.Framework+import Test.QuickCheck.Property               (ioProperty)+import Util  -- | Ciphertext can be decrypted rightInverseProp :: Keypair -> Message -> IO Bool-rightInverseProp (sk1, pk1) (Message bs) = do+rightInverseProp (Keypair sk1 pk1) (Message bs) = do   enc <- boxSeal pk1 bs   return (Just bs == boxSealOpen pk1 sk1 enc)  -- | Cannot decrypt without the correct secret key rightInverseFailureProp1 :: Keypair -> Message -> Perturb -> IO Bool-rightInverseFailureProp1 (sk1, pk1) (Message bs) p = do+rightInverseFailureProp1 (Keypair sk1 pk1) (Message bs) p = do   enc <- boxSeal pk1 bs   return (Nothing == boxSealOpen pk1 (perturb sk1 ([0] <> p)) enc)  -- | Cannot decrypt without the correct public key rightInverseFailureProp2 :: Keypair -> Message -> Perturb -> IO Bool-rightInverseFailureProp2 (sk1, pk1) (Message bs) p = do+rightInverseFailureProp2 (Keypair sk1 pk1) (Message bs) p = do   enc <- boxSeal pk1 bs   return (Nothing == boxSealOpen (perturb pk1 p) sk1 enc)  -- | Cannot decrypt when not sent to you rightInverseFailureProp3 :: Keypair -> Message -> Perturb -> IO Bool-rightInverseFailureProp3 (sk1, pk1) (Message bs) p = do+rightInverseFailureProp3 (Keypair sk1 pk1) (Message bs) p = do   enc <- boxSeal (perturb pk1 p) bs   return (Nothing == boxSealOpen pk1 sk1 enc)  -- | Ciphertext cannot be decrypted (verification failure) if the -- ciphertext is perturbed rightInverseFailureProp4 :: Keypair -> Message -> Perturb -> IO Bool-rightInverseFailureProp4 (sk1, pk1) (Message bs) p = do+rightInverseFailureProp4 (Keypair sk1 pk1) (Message bs) p = do   enc <- boxSeal pk1 bs   return (Nothing == boxSealOpen pk1 sk1 (perturb enc p))  testSealedBox :: Test testSealedBox = buildTest $ do -  (sk1, pk1) <- newKeypair+  kp <- newKeypair    return $ testGroup "... SealedBox" [      testGroup "Can decrypt ciphertext using..." [        testProperty "... public key/secret key"-       $ ioProperty . rightInverseProp (sk1, pk1)+       $ ioProperty . rightInverseProp kp        ],      testGroup "Fail to verify ciphertext when..." [       testProperty "... not using proper secret key"-      $ ioProperty . uncurry (rightInverseFailureProp1 (sk1, pk1)),+      $ ioProperty . uncurry (rightInverseFailureProp1 kp),        testProperty "... not using proper public key"-      $ ioProperty . uncurry (rightInverseFailureProp2 (sk1, pk1)),+      $ ioProperty . uncurry (rightInverseFailureProp2 kp),        testProperty "... not actually sent to you"-      $ ioProperty . uncurry (rightInverseFailureProp3 (sk1, pk1)),+      $ ioProperty . uncurry (rightInverseFailureProp3 kp),        testProperty "... ciphertext has been perturbed"-      $ ioProperty . uncurry ( rightInverseFailureProp4 (sk1, pk1) )+      $ ioProperty . uncurry (rightInverseFailureProp4 kp)       ]     ]
tests/SecretBoxProperties.hs view
@@ -8,9 +8,10 @@ import           Util import           Crypto.Saltine.Core.SecretBox import           Crypto.Saltine.Class-import           Crypto.Saltine.Internal.ByteSizes as Bytes+import           Crypto.Saltine.Internal.SecretBox as Internal  import qualified Data.ByteString                      as S+import           Data.Maybe (fromJust) import           Test.Framework.Providers.QuickCheck2 import           Test.Framework import           Test.QuickCheck (Property, (==>))@@ -18,17 +19,13 @@  instance Arbitrary Nonce where     arbitrary =-        do bs <- S.pack <$> vector Bytes.secretBoxNonce-           maybe (fail "impossible arbitrary failure.") pure (decode bs)+        do bs <- S.pack <$> vector Internal.secretbox_noncebytes+           pure $ fromJust  (decode bs)  instance Arbitrary Key where     arbitrary =-        do bs <- S.pack <$> vector Bytes.secretBoxKey-           maybe (fail "impossible arbitrary failure.") pure (decode bs)-instance Show Key where-    show = show . encode-instance Show Nonce where-    show = show . encode+        do bs <- S.pack <$> vector Internal.secretbox_keybytes+           pure $ fromJust (decode bs)  -- | Ciphertext can be decrypted rightInverseProp :: Key -> Nonce -> Message -> Bool@@ -49,8 +46,9 @@  -- | Ciphertext cannot be decrypted if the tag is perturbed rightInverseTagFailureProp :: Key -> Nonce -> Message -> Message -> Property-rightInverseTagFailureProp k n (Message bs) (Message fakeTag) =+rightInverseTagFailureProp k n (Message bs) (Message fakeTagBs) =   let (realTag, ct) = secretboxDetached k n bs+      fakeTag       = Internal.Au fakeTagBs   in realTag /= fakeTag ==> Nothing == secretboxOpenDetached k n fakeTag ct  -- | Ciphertext cannot be decrypted if the ciphertext is perturbed
tests/SignProperties.hs view
@@ -5,17 +5,28 @@   ) where  import           Util-import           Crypto.Saltine.Core.Sign+import           Crypto.Saltine.Class +import           Crypto.Saltine.Core.Sign+import           Crypto.Saltine.Internal.Sign+import qualified Crypto.Saltine.Core.Box              as Box+import           Crypto.Saltine.Core.ScalarMult       ()+import           Crypto.Saltine.Internal.ScalarMult import qualified Data.ByteString                      as S+import           Data.Maybe                           (fromJust) import           Test.Framework.Providers.QuickCheck2 import           Test.Framework import           Test.QuickCheck  testSign :: Test testSign = buildTest $ do-  (sk1,  pk1) <- newKeypair-  (_sk2, pk2) <- newKeypair+  kp1 <- newKeypair+  let sk1 = secretKey kp1+  let pk1 = publicKey kp1+  kp2 <- newKeypair+  let sk2 = secretKey kp2+  let pk2 = publicKey kp2+  n <- Box.newNonce    return $ testGroup "...Internal.Sign" [ @@ -34,6 +45,15 @@      testProperty "Rejects message with mismatched key w/ detached signature"     $ \(Message bs) -> not (S.null bs) ==>-                         not (signVerifyDetached pk2 (sign sk1 bs) bs)+                         not (signVerifyDetached pk2 (signDetached sk1 bs) bs), +    testProperty "Ed25515 -> Curve25519 conversion produces valid keypairs"+    $ \(Message bs) ->+        let p1 = fromJust . decode . unGE . fromJust $ signPublicKeyToScalarMult pk1+            s1 = fromJust . decode . unSc . fromJust $ signSecretKeyToScalarMult sk1++            p2 = fromJust . decode . unGE . fromJust $ signPublicKeyToScalarMult pk2+            s2 = fromJust . decode . unSc . fromJust $ signSecretKeyToScalarMult sk2++        in  (Box.boxOpen p2 s1 n (Box.box p1 s2 n bs)) == Just bs     ]
tests/StreamProperties.hs view
@@ -23,8 +23,8 @@               ==> S.length (stream k n len) == len,      testProperty "xor munges input"-    $ \(Message bs) -> not (S.null bs)-                       ==> xor k n bs /= bs,+    $ \(ByteString32 bs) ->     -- ByteString32 so we don't fail the test by pure chance, e.g. in case of 1-byte bytestrings+        xor k n bs /= bs,      testProperty "xor is involutive"     $ \(Message bs) -> xor k n (xor k n bs) == bs
tests/Util.hs view
@@ -5,13 +5,12 @@  import           Crypto.Saltine.Class -import           Control.Applicative import           Control.Monad           (replicateM) import qualified Data.ByteString       as S import           Data.Monoid+import           Data.Semigroup          (Semigroup) import           Data.Word (Word8) import           Data.Bits (xor)-import           Data.Semigroup (Semigroup) import           Test.QuickCheck import           GHC.Exts (IsList(..)) @@ -45,7 +44,7 @@             then pure (Perturb (1:bs))             else pure (Perturb bs) -newtype ByteString32 = ByteString32 S.ByteString deriving (Show)+newtype ByteString32 = ByteString32 S.ByteString deriving (Eq,Show)  instance Arbitrary ByteString32 where   arbitrary = ByteString32 . S.pack <$> replicateM 32 arbitrary
+ tests/UtilProperties.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE OverloadedStrings #-}++module UtilProperties (+  testUtils+  ) where++import Test.Framework.Providers.QuickCheck2+import Test.Framework+import Test.QuickCheck+import Util++import qualified Crypto.Saltine.Internal.Util as U++-- | Testing the comparison of keys+keyEquality :: ByteString32 -> Property+keyEquality k@(ByteString32 bs1) = k === k .&. U.compare bs1 bs1++keyInequality :: ByteString32 -> ByteString32 -> Property+keyInequality k1@(ByteString32 bs1) k2@(ByteString32 bs2) =+    k1 /= k2 ==> not $ U.compare bs1 bs2+++testUtils :: Test+testUtils = buildTest $ do+  return $ testGroup "...Utils" [+    testProperty "ByteString equality"      keyEquality,+    testProperty "ByteString inequality"    keyInequality+    ]