saltine 0.1.0.1 → 0.2.2.0
raw patch · 66 files changed
Files
- CHANGELOG.md +51/−0
- README.md +69/−0
- bench/AES256GCMBench.hs +68/−0
- bench/AuthBench.hs +37/−0
- bench/BenchUtils.hs +15/−0
- bench/BoxBench.hs +45/−0
- bench/ChaCha20Poly1305Bench.hs +68/−0
- bench/ChaCha20Poly1305IETFBench.hs +68/−0
- bench/ConstantTimeBench.hs +29/−0
- bench/HashBench.hs +40/−0
- bench/Main.hs +79/−0
- bench/OneTimeAuthBench.hs +37/−0
- bench/PasswordBench.hs +87/−0
- bench/RandomBench.hs +21/−0
- bench/ScalarMultBench.hs +35/−0
- bench/SecretBoxBench.hs +57/−0
- bench/SignBench.hs +53/−0
- bench/StreamBench.hs +37/−0
- bench/XChaCha20Poly1305Bench.hs +68/−0
- saltine.cabal +79/−12
- src/Crypto/Saltine/Class.hs +4/−3
- src/Crypto/Saltine/Core/AEAD.hs +14/−223
- src/Crypto/Saltine/Core/AEAD/AES256GCM.hs +149/−0
- src/Crypto/Saltine/Core/AEAD/ChaCha20Poly1305.hs +134/−0
- src/Crypto/Saltine/Core/AEAD/ChaCha20Poly1305IETF.hs +134/−0
- src/Crypto/Saltine/Core/AEAD/XChaCha20Poly1305.hs +134/−0
- src/Crypto/Saltine/Core/Auth.hs +12/−61
- src/Crypto/Saltine/Core/Box.hs +42/−200
- src/Crypto/Saltine/Core/Hash.hs +59/−55
- src/Crypto/Saltine/Core/OneTimeAuth.hs +11/−59
- src/Crypto/Saltine/Core/Password.hs +247/−0
- src/Crypto/Saltine/Core/ScalarMult.hs +12/−50
- src/Crypto/Saltine/Core/SecretBox.hs +59/−138
- src/Crypto/Saltine/Core/Sign.hs +61/−107
- src/Crypto/Saltine/Core/Stream.hs +11/−64
- src/Crypto/Saltine/Core/Utils.hs +2/−1
- src/Crypto/Saltine/Internal/AEAD/AES256GCM.hs +188/−0
- src/Crypto/Saltine/Internal/AEAD/ChaCha20Poly1305.hs +184/−0
- src/Crypto/Saltine/Internal/AEAD/ChaCha20Poly1305IETF.hs +183/−0
- src/Crypto/Saltine/Internal/AEAD/XChaCha20Poly1305.hs +183/−0
- src/Crypto/Saltine/Internal/Auth.hs +103/−0
- src/Crypto/Saltine/Internal/Box.hs +285/−0
- src/Crypto/Saltine/Internal/ByteSizes.hs +3/−250
- src/Crypto/Saltine/Internal/Hash.hs +182/−0
- src/Crypto/Saltine/Internal/OneTimeAuth.hs +104/−0
- src/Crypto/Saltine/Internal/Password.hs +567/−0
- src/Crypto/Saltine/Internal/ScalarMult.hs +91/−0
- src/Crypto/Saltine/Internal/SecretBox.hs +179/−0
- src/Crypto/Saltine/Internal/Sign.hs +198/−0
- src/Crypto/Saltine/Internal/Stream.hs +107/−0
- src/Crypto/Saltine/Internal/Util.hs +105/−10
- tests/AEAD/AES256GCMProperties.hs +125/−0
- tests/AEAD/ChaCha20Poly1305IETFProperties.hs +128/−0
- tests/AEAD/ChaCha20Poly1305Properties.hs +128/−0
- tests/AEAD/XChaCha20Poly1305Properties.hs +128/−0
- tests/AEADProperties.hs +0/−132
- tests/BoxProperties.hs +21/−23
- tests/HashProperties.hs +53/−0
- tests/Main.hs +15/−3
- tests/PasswordProperties.hs +97/−0
- tests/SealedBoxProperties.hs +17/−19
- tests/SecretBoxProperties.hs +8/−10
- tests/SignProperties.hs +24/−4
- tests/StreamProperties.hs +2/−2
- tests/Util.hs +2/−3
- tests/UtilProperties.hs +28/−0
+ 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 [](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+ ]