Squashed 'miscellaneous/saltine/' content from commit 6930947c5

git-subtree-dir: miscellaneous/saltine
git-subtree-split: 6930947c556970daf8bdac71f9bdc3bb592b80c9
This commit is contained in:
voidlizard 2024-10-07 05:03:46 +03:00
commit 2702905cfd
74 changed files with 7275 additions and 0 deletions

53
.github/workflows/haskell.yml vendored Normal file
View File

@ -0,0 +1,53 @@
name: Haskell CI
on: [push, pull_request]
jobs:
build:
if: "!contains(github.event.head_commit.message, 'skip ci') && !contains(github.event.head_commit.message, 'ci skip')"
runs-on: ubuntu-22.04
strategy:
matrix:
ghc-version: ['8.0.2','8.2.2','8.4.4','8.6.5','8.8.4','8.10.7','9.0.2', '9.2.6', '9.4.4']
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: Workaround runner image issue
if: ${{ runner.os == 'Linux' }}
# https://github.com/actions/runner-images/issues/7061
run: sudo chown -R $USER /usr/local/.ghcup
- uses: haskell/actions/setup@v2
with:
ghc-version: ${{ matrix.ghc-version }}
cabal-version: '3.8'
- name: Cache
uses: actions/cache@v2
env:
cache-name: cache-cabal-ghc-${{ matrix.ghc-version }}
with:
path: ~/.cabal
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
- name: Install libsodium
run: |
curl -# -L https://github.com/jedisct1/libsodium/releases/download/$SODIUMVER-RELEASE/libsodium-$SODIUMVER.tar.gz | tar xzf -
(cd libsodium-$SODIUMVER && ./autogen.sh && ./configure && make check && sudo make install && sudo ldconfig)
env:
SODIUMVER: 1.0.18
- name: Install dependencies
run: |
cabal update
cabal build --only-dependencies --enable-tests --enable-benchmarks
- name: Build
run: cabal build --enable-tests --enable-benchmarks all
- name: Run tests
run: cabal test all
- name: Install
run: cabal install --lib

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.hsenv*
dist*

44
CHANGELOG.md Normal file
View File

@ -0,0 +1,44 @@
# 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.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)

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013 Joseph Abrahamson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

20
Makefile Normal file
View File

@ -0,0 +1,20 @@
deps:
cabal install --only-dependencies --enable-test -j4
configure: deps
cabal configure --enable-test
build: configure
cabal build
test: build
cabal test
clean:
cabal clean
all: test
.PHONY: deps configure build test all

69
README.md Normal file
View File

@ -0,0 +1,69 @@
# Saltine 0.2.1.0 [![Hackage version](https://img.shields.io/hackage/v/saltine.svg?colorB=4FB900)](https://hackage.haskell.org/package/saltine)
A Haskell binding for @jedisct1's portable binding for djb's
NaCl. **This is an early release.** Please try it out, but don't just
yet stake your life or job on it.
It is imperative you call `sodiumInit` before using any other function.
``` haskell
import Crypto.Saltine
import Crypto.Saltine.Core.SecretBox
import qualified Data.ByteString.Char8 as BSC8
main = do
sodiumInit
k <- newKey
n <- newNonce
let ciphertext = secretbox k n (BSC8.pack "foobar")
print $ secretboxOpen k n ciphertext
-- Just "foobar"
```
In
[*The Security Impact of a New Cryptographic Library*](http://cryptojedi.org/papers/coolnacl-20111201.pdf)
Bernstein, Lange, and Schwabe argue that high-level cryptographic
libraries eliminate whole spaces of cryptographic disasters which are
nigh inevitable whenever programmers use low-level crypto primitives.
* [Security Stack Exchange: Why Shouldn't We Roll Our Own?](http://security.stackexchange.com/questions/18197/why-shouldnt-we-roll-our-own)
* [Hacker News on "All the Crypto Code You've Ever Written is Probably Broken"](https://news.ycombinator.com/item?id=4779015)
* [Stack Overflow: When can you trust yourself to implement cryptography based solutions?](http://stackoverflow.com/questions/1914257/when-can-you-trust-yourself-to-implement-cryptography-based-solutions)
* [Coding Horror: Why isn't my encryption... encrypting?](http://www.codinghorror.com/blog/2009/05/why-isnt-my-encryption-encrypting.html)
Crypto is complicated, so pre-rolled solutions are important
prevention mechanisms.
[NaCl](http://nacl.cr.yp.to/) is Bernstein, Lange, and Schwabe's
solution: a high-level, performant cryptography library with a no-fuss
interface. [Saltine](http://github.com/tel/saltine) is a Haskell
binding to NaCl (via
[`libsodium`](https://github.com/jedisct1/libsodium)) which hopes to
provide even more simplicity and safety to the usage of cryptography.
Note that it's still possible to shoot yourself in the foot pretty
easily using Saltine. Nonces must always be unique which must be managed
by the library user.
[`Crypto.Saltine.Core.Stream`](https://github.com/tel/saltine/blob/master/src/Crypto/Saltine/Core/Stream.hs)
produces messages which can beundetectably tampered with in-flight.
Keys are insecurely read from disk—they may be copied and then paged
back to disk.
When uncertain, use [`Crypto.Saltine.Core.SecretBox`](https://github.com/tel/saltine/blob/master/src/Crypto/Saltine/Core/SecretBox.hs)
and [`Crypto.Saltine.Core.Box`](https://github.com/tel/saltine/blob/master/src/Crypto/Saltine/Core/Box.hs).
If you can think of ways to use Haskell's type system to enforce
security invariants, please suggest them.
To use it on Windows systems, download
[a prebuild libsodium-\*-stable-mingw.tar.gz file](https://download.libsodium.org/libsodium/releases/)
and copy the files in `libsodium-win64` into the equivalent places
in `C:\Program Files\Haskell Platform\*\mingw`. Then just add saltine
to your cabal file and watch it go.
Tested with [`libsodium-1.0.18`](https://download.libsodium.org/libsodium/releases/).
Inspired by @thoughtpolice's
[`salt`](http://github.com/thoughtpolice/salt) library. `salt` also
binds to NaCl, but uses a Haskell managed version of djb's code
instead of `libsodium`.

3
Setup.hs Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env runhaskell
import Distribution.Simple
main = defaultMain

68
bench/AES256GCMBench.hs Normal file
View File

@ -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
]
]

37
bench/AuthBench.hs Normal file
View File

@ -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
]
]

15
bench/BenchUtils.hs Normal file
View File

@ -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")

45
bench/BoxBench.hs Normal file
View File

@ -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
]
]

View File

@ -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
]
]

View File

@ -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
]
]

View File

@ -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
]

40
bench/HashBench.hs Normal file
View File

@ -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
]
]

79
bench/Main.hs Normal file
View File

@ -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
]

37
bench/OneTimeAuthBench.hs Normal file
View File

@ -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
]
]

87
bench/PasswordBench.hs Normal file
View File

@ -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
]
]
]

21
bench/RandomBench.hs Normal file
View File

@ -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)
]

35
bench/ScalarMultBench.hs Normal file
View File

@ -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
]

57
bench/SecretBoxBench.hs Normal file
View File

@ -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
]
]

53
bench/SignBench.hs Normal file
View File

@ -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
]
]

37
bench/StreamBench.hs Normal file
View File

@ -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
]
]

View File

@ -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
]
]

159
saltine.cabal Normal file
View File

@ -0,0 +1,159 @@
cabal-version: 2.0
name: saltine
version: 0.2.1.0
synopsis: Cryptography that's easy to digest (NaCl/libsodium bindings).
description:
/NaCl/ (pronounced \"salt\") is a new easy-to-use high-speed software
library for network communication, encryption, decryption,
signatures, etc. NaCl's goal is to provide all of the core
operations needed to build higher-level cryptographic tools.
.
<http://nacl.cr.yp.to/>
.
/Sodium/ is a portable, cross-compilable, installable, packageable
crypto library based on NaCl, with a compatible API.
.
<https://github.com/jedisct1/libsodium>
.
/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
maintainer: Max Amanshauser <max@lambdalifting.org>
bug-reports: http://github.com/tel/saltine/issues
copyright: Copyright (c) Joseph Abrahamson 2013
category: Cryptography
build-type: Simple
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
location: https://github.com/tel/saltine.git
library
hs-source-dirs: src
exposed-modules:
Crypto.Saltine
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
Crypto.Saltine.Core.OneTimeAuth
Crypto.Saltine.Core.Sign
Crypto.Saltine.Core.Hash
Crypto.Saltine.Core.ScalarMult
Crypto.Saltine.Core.Password
Crypto.Saltine.Core.Utils
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.18
cc-options: -Wall
ghc-options: -Wall -funbox-strict-fields
default-language: Haskell2010
build-depends:
base >= 4.5 && < 5
, bytestring >= 0.10.8 && < 0.12
, deepseq ^>= 1.4
, profunctors >= 5.3 && < 5.7
, hashable
, text ^>= 1.2 || ^>= 2.0
test-suite tests
type: exitcode-stdio-1.0
main-is: Main.hs
other-modules:
AuthProperties
BoxProperties
HashProperties
OneTimeAuthProperties
PasswordProperties
ScalarMultProperties
SecretBoxProperties
SealedBoxProperties
SignProperties
StreamProperties
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.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

23
src/Crypto/Saltine.hs Normal file
View File

@ -0,0 +1,23 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE TypeFamilies #-}
module Crypto.Saltine (
sodiumInit
) where
import Foreign.C
-- | Runs Sodiums's initialization routine. This must be called before
-- using any other function. It is thread-safe since libsodium 1.0.11.
sodiumInit :: IO ()
sodiumInit = do
err <- c_sodiumInit
case err of
0 -> -- everything went well
return ()
1 -> -- already initialized, we're good
return ()
_ -> -- some kind of failure
error "Crypto.Saltine.sodiumInit"
foreign import ccall "sodium_init" c_sodiumInit :: IO CInt

View File

@ -0,0 +1,55 @@
-- {-# LANGUAGE FlexibleInstances #-}
-- |
-- Module : Crypto.Saltine.Class
-- Copyright : (c) Joseph Abrahamson 2013
-- License : MIT
--
-- Maintainer : me@jspha.com
-- Stability : experimental
-- Portability : non-portable
--
-- Saltine type classes
module Crypto.Saltine.Class (
IsEncoding (..),
IsNonce (..)
) where
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
-- type @Prism' ByteString a@ compatible with "Control.Lens" and
-- is automatically deduced.
class IsEncoding a where
encode :: a -> ByteString
decode :: ByteString -> Maybe a
encoded :: (Choice p, Applicative f)
=> p a (f a) -> p ByteString (f ByteString)
encoded = prism' encode decode
{-# INLINE encoded #-}
-- | A generic class for interacting with nonces.
class IsNonce n where
zero :: n
-- ^ Some privileged nonce value.
nudge :: n -> n
-- ^ Some perturbation on nonces such that @n /= nudge n@ with high
-- probability. Since nonces are finite, repeats may happen in
-- particularly small cases, but no nonces in Saltine are so
-- small. This is not guaranteed to be difficult to predict---if a
-- nonce had an `Enum` instance `succ` would be a good
-- implementation excepting that `succ` is partial.
-- Copied over from Control.Lens
prism' :: (Applicative f, Choice p) =>
(a1 -> a) -> (a -> Maybe a2) -> p a2 (f a1) -> p a (f a)
prism' bs sma = prism bs (\s -> maybe (Left s) Right (sma s))
{-# INLINE prism' #-}
prism :: (Applicative f, Choice p) =>
(a2 -> a1) -> (a -> Either a1 a3) -> p a3 (f a2) -> p a (f a1)
prism bt seta = dimap seta (either pure (fmap bt)) . right'
{-# INLINE prism #-}

View File

@ -0,0 +1,47 @@
-- |
-- Module : Crypto.Saltine.Core.AEAD
-- 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"
--
-- The 'aead' function encrypts and authenticates a message
-- 'ByteString' and additional authenticated data 'ByteString'
-- using a secret key and a nonce. The 'aeadOpen'
-- function verifies and decrypts a ciphertext 'ByteString' using a
-- secret key and a nonce. If the ciphertext fails validation,
-- 'aeadOpen' returns 'Nothing'.
--
-- The "Crypto.Saltine.Core.AEAD" module is designed to meet
-- the standard notions of privacy and authenticity for a secret-key
-- authenticated-encryption scheme using nonces. For formal
-- definitions see, e.g., Bellare and Namprempre, "Authenticated
-- encryption: relations among notions and analysis of the generic
-- composition paradigm," Lecture Notes in Computer Science 1976
-- (2000), 531545, <http://www-cse.ucsd.edu/~mihir/papers/oem.html>.
--
-- 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. 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 (
module X
) where
import Crypto.Saltine.Core.AEAD.XChaCha20Poly1305 as X

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,84 @@
-- |
-- Module : Crypto.Saltine.Core.Auth
-- Copyright : (c) Joseph Abrahamson 2013
-- License : MIT
--
-- Maintainer : me@jspha.com
-- Stability : experimental
-- Portability : non-portable
--
-- Secret-key message authentication:
-- "Crypto.Saltine.Core.Auth"
--
-- The 'auth' function authenticates a message 'ByteString' using a
-- 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.
--
-- The 'auth' function, viewed as a function of the message for a
-- uniform random key, is designed to meet the standard notion of
-- unforgeability. This means that an attacker cannot find
-- authenticators for any messages not authenticated by the sender,
-- even if the attacker has adaptively influenced the messages
-- authenticated by the sender. For a formal definition see, e.g.,
-- Section 2.4 of Bellare, Kilian, and Rogaway, \"The security of the
-- cipher block chaining message authentication code,\" Journal of
-- Computer and System Sciences 61 (2000), 362399;
-- <http://www-cse.ucsd.edu/~mihir/papers/cbc.html>.
--
-- Saltine does not make any promises regarding \"strong\"
-- unforgeability; perhaps one valid authenticator can be converted
-- into another valid authenticator for the same message. NaCl also
-- does not make any promises regarding \"truncated unforgeability.\"
--
-- "Crypto.Saltine.Core.Auth" is currently an implementation of
-- HMAC-SHA-512-256, i.e., the first 256 bits of
-- HMAC-SHA-512. HMAC-SHA-512-256 is conjectured to meet the standard
-- notion of unforgeability.
--
-- This is version 2010.08.30 of the auth.html web page.
module Crypto.Saltine.Core.Auth (
Key, Authenticator,
newKey,
auth, verify
) where
import Crypto.Saltine.Internal.Auth
( c_auth
, c_auth_verify
, Key(..)
, Authenticator(..)
)
import Crypto.Saltine.Internal.Util as U
import Data.ByteString (ByteString)
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.auth_keybytes
-- | Computes an keyed authenticator 'ByteString' from a message. It
-- is infeasible to forge these authenticators without the key, even
-- if an attacker observes many authenticators and messages and has
-- the ability to influence the messages sent.
auth :: Key
-> ByteString
-- ^ Message
-> Authenticator
auth (Key key) msg =
Au . snd . buildUnsafeByteString Bytes.auth_bytes $ \pa ->
constByteStrings [key, msg] $ \[(pk, _), (pm, mlen)] ->
c_auth pa pm (fromIntegral mlen) pk
-- | Checks to see if an authenticator is a correct proof that a
-- message was signed by some key.
verify :: Key
-> Authenticator
-> ByteString
-- ^ Message
-> Bool
-- ^ Is this message authentic?
verify (Key key) (Au a) msg =
unsafeDidSucceed $ constByteStrings [key, msg, a] $ \[(pk, _), (pm, mlen), (pa, _)] ->
return $ c_auth_verify pa pm (fromIntegral mlen) pk

View File

@ -0,0 +1,231 @@
-- |
-- Module : Crypto.Saltine.Core.Box
-- Copyright : (c) Joseph Abrahamson 2013
-- License : MIT
--
-- Maintainer : me@jspha.com
-- Stability : experimental
-- Portability : non-portable
--
-- Public-key cryptography abstraction:
-- "Crypto.Saltine.Core.Box"
--
-- This module consists of functions dealing with two public-key
-- cryptography concepts in libsodium.
--
-- The first one is an authenticated encryption scheme. In this
-- scheme, the 'box' function encrypts and authenticates a message
-- 'ByteString' using the sender's secret key, the receiver's public
-- key, and a nonce. The 'boxOpen' function verifies and decrypts a
-- ciphertext 'ByteString' using the receiver's secret key, the
-- sender's public key, and a nonce. If the ciphertext fails
-- verification, 'boxOpen' returns 'Nothing'.
--
-- The set of box functions is designed to meet the
-- standard notions of privacy and third-party unforgeability for a
-- public-key authenticated-encryption scheme using nonces. For formal
-- definitions see, e.g., Jee Hea An, "Authenticated encryption in the
-- public-key setting: security notions and analyses,"
-- <http://eprint.iacr.org/2001/079>.
--
-- Distinct messages between the same @{sender, receiver}@ set are
-- required to have distinct nonces. For example, the
-- lexicographically smaller public key can use nonce 1 for its first
-- message to the other key, nonce 3 for its second message, nonce 5
-- for its third message, etc., while the lexicographically larger
-- public key uses nonce 2 for its first message to the other key,
-- nonce 4 for its second message, nonce 6 for its third message,
-- etc. Nonces are long enough that randomly generated nonces have
-- negligible risk of collision.
--
-- There is no harm in having the same nonce for different messages if
-- the @{sender, receiver}@ sets are different. This is true even if
-- the sets overlap. For example, a sender can use the same nonce for
-- two different messages if the messages are sent to two different
-- public keys.
--
-- The second concept is sealed boxes, which provide encryption and
-- preservation of integrity, but not authentication. Technically,
-- the sender of a message generates a keypair, uses the regular
-- box mechanism, attaches the public key to the message and then
-- immediately destroys the private key. This is useful, e.g. when
-- the receiver cannot know the sender's public key in advance and
-- hence cannot use the regular box functions, or when you want to
-- send messages anonymously.
--
-- The "Crypto.Saltine.Core.Box" module is not meant to provide
-- non-repudiation. On the contrary: the crypto_box function
-- guarantees repudiability. A receiver can freely modify a boxed
-- message, and therefore cannot convince third parties that this
-- particular message came from the sender. The sender and receiver
-- are nevertheless protected against forgeries by other parties. In
-- the terminology of
-- <http://groups.google.com/group/sci.crypt/msg/ec5c18b23b11d82c>,
-- crypto_box uses "public-key authenticators" rather than "public-key
-- signatures."
--
-- Users who want public verifiability (or receiver-assisted public
-- verifiability) should instead use signatures (or
-- signcryption). Signatures are documented in the
-- "Crypto.Saltine.Core.Sign" module.
--
-- "Crypto.Saltine.Core.Box" is @curve25519xsalsa20poly1305@, a
-- particular combination of Curve25519, Salsa20, and Poly1305
-- specified in "Cryptography in NaCl"
-- (<http://nacl.cr.yp.to/valid.html>). This function is conjectured
-- to meet the standard notions of privacy and third-party
-- unforgeability.
--
-- This is version 2010.08.30 of the box.html web page.
module Crypto.Saltine.Core.Box (
SecretKey, PublicKey, Keypair(..), CombinedKey, Nonce,
newKeypair, beforeNM, newNonce,
box, boxOpen,
boxAfterNM, boxOpenAfterNM,
boxSeal, boxSealOpen
) where
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)
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.box_publickeybytes $ \pkbuf ->
buildUnsafeByteString' Bytes.box_secretkeybytes $ \skbuf ->
c_box_keypair pkbuf skbuf
return $ Keypair (SK sk) (PK pk)
-- | Randomly generates a nonce for usage with 'box' and 'boxOpen'.
newNonce :: IO Nonce
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.box_beforenmbytes $ \ckbuf ->
constByteStrings [pk, sk] $ \[(ppk, _), (psk, _)] ->
c_box_beforenm ckbuf ppk psk
-- | Encrypts a message for sending to the owner of the public
-- key. They must have your public key in order to decrypt the
-- message. It is infeasible for an attacker to decrypt the message so
-- long as the 'Nonce' is not repeated.
box :: PublicKey
-> SecretKey
-> Nonce
-> ByteString
-- ^ Message
-> ByteString
-- ^ Ciphertext (incl. authentication tag)
box (PK pk) (SK sk) (Nonce nonce) msg =
snd . buildUnsafeByteString bufSize $ \pc ->
constByteStrings [pk, sk, msg, nonce] $ \
[(ppk, _), (psk, _), (pm, _), (pn, _)] ->
c_box_easy pc pm (fromIntegral msgLen) pn ppk psk
where
bufSize = S.length msg + Bytes.box_macbytes
msgLen = S.length msg
-- | Decrypts a message sent from the owner of the public key. They
-- must have encrypted it using your public key. Returns 'Nothing' if
-- the keys and message do not match.
boxOpen :: PublicKey -> SecretKey -> Nonce
-> ByteString
-- ^ Ciphertext (incl. authentication tag)
-> Maybe ByteString
-- ^ Message
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
hush . handleErrno err $ vec
-- | 'box' using a 'CombinedKey' and thus faster.
boxAfterNM :: CombinedKey
-> Nonce
-> ByteString
-- ^ Message
-> ByteString
-- ^ Ciphertext (incl. authentication tag)
boxAfterNM (CK ck) (Nonce nonce) msg =
snd . buildUnsafeByteString bufSize $ \pc ->
constByteStrings [ck, msg, nonce] $ \
[(pck, _), (pm, _), (pn, _)] ->
c_box_easy_afternm pc pm (fromIntegral msgLen) pn pck
where
bufSize = S.length msg + Bytes.box_macbytes
msgLen = S.length msg
-- | 'boxOpen' using a 'CombinedKey' and is thus faster.
boxOpenAfterNM :: CombinedKey
-> Nonce
-> ByteString
-- ^ Ciphertext (incl. authentication tag)
-> Maybe ByteString
-- ^ Message
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
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.box_sealbytes
msgLen = S.length msg
-- | Decrypts a sealed box message. The message must have been
-- encrypted using the receiver's public key.
-- Returns 'Nothing' if keys and message do not match or integrity
-- is violated.
boxSealOpen :: PublicKey
-> SecretKey
-> ByteString
-- ^ Ciphertext
-> Maybe ByteString
-- ^ Message
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
hush . handleErrno err $ vec

View File

@ -0,0 +1,103 @@
-- |
-- Module : Crypto.Saltine.Core.Hash
-- Copyright : (c) Joseph Abrahamson 2013
-- License : MIT
--
-- Maintainer : me@jspha.com
-- Stability : experimental
-- Portability : non-portable
--
-- Hashing: "Crypto.Saltine.Core.Hash"
--
-- The 'hash' function hashes a message 'ByteString' and returns a
-- hash. Hashes are always of length 'Bytes.hash'. The 'shorthash'
-- function hashes a message 'ByteString' with respect to a secret key
-- and returns a very short hash. Short hashes are always of length
-- 'Bytes.shorthash'.
--
-- The 'hash' function is designed to be usable as a strong component
-- of DSA, RSA-PSS, key derivation, hash-based message-authentication
-- codes, hash-based ciphers, and various other common
-- applications. "Strong" means that the security of these
-- applications, when instantiated with 'hash', is the same as the
-- security of the applications against generic attacks. In
-- particular, the 'hash' function is designed to make finding
-- collisions difficult.
--
-- 'hash' is currently an implementation of SHA-512. 'shorthash' is
-- currently an implementation of SipHash-2-4
-- (<https://131002.net/siphash/>).
--
-- There has been considerable degradation of public confidence in the
-- security conjectures for many hash functions, including
-- SHA-512. However, for the moment, there do not appear to be
-- 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 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 (
ShorthashKey,
hash,
shorthash, newShorthashKey,
GenerichashKey,
newGenerichashKey,
GenerichashOutLen,
generichashOutLen, generichash
) where
import Crypto.Saltine.Internal.Hash
( c_hash
, c_generichash
, shorthash
, ShorthashKey(..)
, GenerichashKey(..)
, GenerichashOutLen(..)
)
import Crypto.Saltine.Internal.Util as U
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
-- attack.
hash :: ByteString
-- ^ Message
-> ByteString
-- ^ Hash
hash m = snd . buildUnsafeByteString Bytes.hash_bytes $ \ph ->
constByteStrings [m] $ \[(pm, _)] -> c_hash ph pm (fromIntegral $ S.length m)
-- | Randomly generates a new key for 'shorthash'.
newShorthashKey :: IO ShorthashKey
newShorthashKey = ShK <$> randomByteString Bytes.shorthash_keybytes
-- | 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
-- | 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
-- | 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)

View File

@ -0,0 +1,80 @@
-- |
-- Module : Crypto.Saltine.Core.OneTimeAuth
-- Copyright : (c) Joseph Abrahamson 2013
-- License : MIT
--
-- Maintainer : me@jspha.com
-- Stability : experimental
-- Portability : non-portable
--
-- Secret-key single-message authentication:
-- "Crypto.Saltine.Core.OneTimeAuth"
--
-- The 'auth' function authenticates a message 'ByteString' using a
-- 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.
--
-- The 'auth' function, viewed as a function of the message for a
-- uniform random key, is designed to meet the standard notion of
-- unforgeability after a single message. After the sender
-- authenticates one message, an attacker cannot find authenticators
-- for any other messages.
--
-- The sender must not use 'auth' to authenticate more than one
-- message under the same key. Authenticators for two messages under
-- the same key should be expected to reveal enough information to
-- allow forgeries of authenticators on other messages.
--
-- "Crypto.Saltine.Core.OneTimeAuth" is
-- @crypto_onetimeauth_poly1305@, an authenticator specified in
-- "Cryptography in NaCl" (<http://nacl.cr.yp.to/valid.html>), Section
-- 9. This authenticator is proven to meet the standard notion of
-- unforgeability after a single message.
--
-- This is version 2010.08.30 of the onetimeauth.html web page.
module Crypto.Saltine.Core.OneTimeAuth (
Key, Authenticator,
newKey,
auth, verify
) where
import Crypto.Saltine.Internal.OneTimeAuth
( c_onetimeauth
, c_onetimeauth_verify
, Key(..)
, Authenticator(..)
)
import Crypto.Saltine.Internal.Util as U
import Data.ByteString (ByteString)
import qualified Crypto.Saltine.Internal.OneTimeAuth as Bytes
import qualified Data.ByteString as S
-- | Creates a random key of the correct size for 'auth' and 'verify'.
newKey :: IO Key
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
-- never used twice.
auth :: Key
-> ByteString
-- ^ Message
-> Authenticator
auth (Key key) msg =
Au . snd . buildUnsafeByteString Bytes.onetimeauth_bytes $ \pa ->
constByteStrings [key, msg] $ \[(pk, _), (pm, _)] ->
c_onetimeauth pa pm (fromIntegral $ S.length msg) pk
-- | Verifies that an 'Authenticator' matches a given message and key.
verify :: Key
-> Authenticator
-> ByteString
-- ^ Message
-> Bool
-- ^ Is this message authentic?
verify (Key key) (Au a) msg =
unsafeDidSucceed $ constByteStrings [key, msg, a] $ \
[(pk, _), (pm, _), (pa, _)] ->
return $ c_onetimeauth_verify pa pm (fromIntegral $ S.length msg) pk

View File

@ -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

View File

@ -0,0 +1,79 @@
{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving, DeriveGeneric #-}
-- |
-- Module : Crypto.Saltine.Core.ScalarMult
-- Copyright : (c) Joseph Abrahamson 2013
-- License : MIT
--
-- Maintainer : me@jspha.com
-- Stability : experimental
-- Portability : non-portable
--
-- Scalar multiplication: "Crypto.Saltine.Core.ScalarMult"
--
-- The 'mult' function multiplies a group element by an integer of
-- length 'Bytes.multScalar'. It returns the resulting group element
-- of length 'Bytes.mult'. The 'multBase' function multiplies a
-- standard group element by an integer of length
-- 'Bytes.multScalar'. It returns the resulting group element of
-- length 'Bytes.mult'.
--
-- The correspondence between strings and group elements depends on
-- the primitive implemented by 'mult'. The correspondence is not
-- necessarily injective in either direction, but it is compatible
-- with scalar multiplication in the group. The correspondence does
-- not necessarily include all group elements, but it does include all
-- strings; i.e., every string represents at least one group element.
--
-- The correspondence between strings and integers also depends on the
-- primitive implemented by 'mult'. Every string represents at least
-- one integer.
--
-- 'mult' is designed to be strong as a component of various
-- well-known \"hashed DiffieHellman\" applications. In particular,
-- it is designed to make the \"computational DiffieHellman\" problem
-- (CDH) difficult with respect to the standard base. 'mult' is also
-- designed to make CDH difficult with respect to other nontrivial
-- bases. In particular, if a represented group element has small
-- order, then it is annihilated by all represented scalars. This
-- feature allows protocols to avoid validating membership in the
-- subgroup generated by the standard base.
--
-- NaCl does not make any promises regarding the \"decisional
-- DiffieHellman\" problem (DDH), the \"static DiffieHellman\"
-- problem (SDH), etc. Users are responsible for hashing group
-- elements.
--
-- 'mult' is the function @crypto_scalarmult_curve25519@ specified in
-- \"Cryptography in NaCl\", Sections 2, 3, and 4
-- (<http://nacl.cr.yp.to/valid.html>). This function is conjectured
-- to be strong. For background see Bernstein, \"Curve25519: new
-- Diffie-Hellman speed records,\" Lecture Notes in Computer Science
-- 3958 (2006), 207228, <http://cr.yp.to/papers.html#curve25519>.
--
-- This is version 2010.08.30 of the scalarmult.html web page.
module Crypto.Saltine.Core.ScalarMult (
Scalar, GroupElement,
mult, multBase
) where
import Crypto.Saltine.Internal.Util
import Crypto.Saltine.Internal.ScalarMult
( c_scalarmult
, c_scalarmult_base
, GroupElement(..)
, Scalar(..)
)
import qualified Crypto.Saltine.Internal.ScalarMult as Bytes
mult :: Scalar -> GroupElement -> GroupElement
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.scalarmult_bytes $ \pq ->
constByteStrings [n] $ \[(pn, _)] ->
c_scalarmult_base pq pn

View File

@ -0,0 +1,146 @@
-- |
-- Module : Crypto.Saltine.Core.SecretBox
-- Copyright : (c) Joseph Abrahamson 2013
-- License : MIT
--
-- Maintainer : max@lambdalifting.org
-- Stability : experimental
-- Portability : non-portable
--
-- Secret-key authenticated encryption:
-- "Crypto.Saltine.Core.SecretBox"
--
-- The 'secretbox' function encrypts and authenticates a message
-- 'ByteString' using a secret key and a nonce. The 'secretboxOpen'
-- function verifies and decrypts a ciphertext 'ByteString' using a
-- secret key and a nonce. If the ciphertext fails validation,
-- 'secretboxOpen' returns 'Nothing'.
--
-- The "Crypto.Saltine.Core.SecretBox" module is designed to meet
-- the standard notions of privacy and authenticity for a secret-key
-- authenticated-encryption scheme using nonces. For formal
-- definitions see, e.g., Bellare and Namprempre, "Authenticated
-- encryption: relations among notions and analysis of the generic
-- composition paradigm," Lecture Notes in Computer Science 1976
-- (2000), 531545, <http://www-cse.ucsd.edu/~mihir/papers/oem.html>.
--
-- 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.
--
-- "Crypto.Saltine.Core.SecretBox" is
-- @crypto_secretbox_xsalsa20poly1305@, a particular combination of
-- Salsa20 and Poly1305 specified in \"Cryptography in NaCl\"
-- (<http://nacl.cr.yp.to/valid.html>). This function is conjectured
-- to meet the standard notions of privacy and authenticity.
--
-- This is version 2010.08.30 of the secretbox.html web page.
module Crypto.Saltine.Core.SecretBox (
Key, Nonce, Authenticator,
secretbox, secretboxOpen,
secretboxDetached, secretboxOpenDetached,
newKey, newNonce
) where
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 qualified Crypto.Saltine.Internal.SecretBox as Bytes
import qualified Data.ByteString as S
-- | Creates a random key of the correct size for 'secretbox'.
newKey :: IO Key
newKey = Key <$> randomByteString Bytes.secretbox_keybytes
-- | Creates a random nonce of the correct size for 'secretbox'.
newNonce :: IO Nonce
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 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.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
-> (Authenticator,ByteString)
-- ^ (Authentication Tag, Ciphertext)
secretboxDetached (Key key) (Nonce nonce) msg =
buildUnsafeByteString ctLen $ \pc ->
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.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 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.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
-> 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] $ \
[(pk, _), (pc, _), (pt, _), (pn, _)] ->
c_secretbox_open_detached pm pc pt (fromIntegral len) pn pk
in hush . handleErrno err $ vec
where len = S.length cipher

View File

@ -0,0 +1,131 @@
-- |
-- Module : Crypto.Saltine.Core.Sign
-- Copyright : (c) Joseph Abrahamson 2013
-- License : MIT
--
-- Maintainer : me@jspha.com
-- Stability : experimental
-- Portability : non-portable
--
-- Signatures: "Crypto.Saltine.Core.Sign"
--
-- The 'newKeypair' function randomly generates a secret key and a
-- corresponding public key. The 'sign' function signs a message
-- 'ByteString' using the signer's secret key and returns the
-- resulting signed message. The 'signOpen' function verifies the
-- signature in a signed message using the signer's public key then
-- returns the message without its signature.
--
-- "Crypto.Saltine.Core.Sign" is an EdDSA signature using
-- elliptic-curve Curve25519 (see: <http://ed25519.cr.yp.to/>). See
-- also, \"Daniel J. Bernstein, Niels Duif, Tanja Lange, Peter
-- Schwabe, Bo-Yin Yang. High-speed high-security signatures. Journal
-- of Cryptographic Engineering 2 (2012), 7789.\"
-- <http://ed25519.cr.yp.to/ed25519-20110926.pdf>.
--
-- This is current information as of 2013 June 6.
module Crypto.Saltine.Core.Sign (
SecretKey, PublicKey, Keypair(..), Signature,
newKeypair,
sign, signOpen,
signDetached, signVerifyDetached
) where
import Crypto.Saltine.Internal.Sign
( c_sign_keypair
, c_sign
, c_sign_open
, c_sign_detached
, c_sign_verify_detached
, 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
import qualified Crypto.Saltine.Internal.Sign as Bytes
import qualified Data.ByteString as S
-- | Creates a random key of the correct size for 'sign' and
-- 'signOpen' of form @(secretKey, publicKey)@.
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.sign_publickeybytes $ \pkbuf ->
buildUnsafeByteString' Bytes.sign_secretkeybytes $ \skbuf ->
c_sign_keypair pkbuf skbuf
return $ Keypair (SK sk) (PK pk)
-- | Augments a message with a signature forming a \"signed
-- message\".
sign :: SecretKey
-> ByteString
-- ^ Message
-> ByteString
-- ^ Signed message
sign (SK k) m = unsafePerformIO $
alloca $ \psmlen -> do
(_err, sm) <- buildUnsafeByteString' (len + Bytes.sign_bytes) $ \psmbuf ->
constByteStrings [k, m] $ \[(pk, _), (pm, _)] ->
c_sign psmbuf psmlen pm (fromIntegral len) pk
smlen <- peek psmlen
return $ S.take (fromIntegral smlen) sm
where len = S.length m
-- | Checks a \"signed message\" returning 'Just' the original message
-- iff the signature was generated using the 'SecretKey' corresponding
-- to the given 'PublicKey'. Returns 'Nothing' otherwise.
signOpen :: PublicKey
-> ByteString
-- ^ Signed message
-> Maybe ByteString
-- ^ Maybe the restored message
signOpen (PK k) sm = unsafePerformIO $
alloca $ \pmlen -> do
(err, m) <- buildUnsafeByteString' smlen $ \pmbuf ->
constByteStrings [k, sm] $ \[(pk, _), (psm, _)] ->
c_sign_open pmbuf pmlen psm (fromIntegral smlen) pk
mlen <- peek pmlen
case err of
0 -> return $ Just $ S.take (fromIntegral mlen) m
_ -> return Nothing
where smlen = S.length sm
-- | Returns just the signature for a message using a SecretKey.
signDetached :: SecretKey
-> ByteString
-- ^ Message
-> Signature
-- ^ Signature
signDetached (SK k) m = unsafePerformIO $
alloca $ \psmlen -> do
(_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 $ 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
-> Signature
-- ^ Signature
-> ByteString
-- ^ Message (not signed)
-> Bool
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

View File

@ -0,0 +1,103 @@
-- |
-- Module : Crypto.Saltine.Core.Stream
-- Copyright : (c) Joseph Abrahamson 2013
-- License : MIT
--
-- Maintainer : me@jspha.com
-- Stability : experimental
-- Portability : non-portable
--
-- Secret-key encryption:
-- "Crypto.Saltine.Core.Stream"
--
-- The 'stream' function produces a sized stream 'ByteString' as a
-- function of a secret key and a nonce. The 'xor' function encrypts a
-- message 'ByteString' using a secret key and a nonce. The 'xor'
-- function guarantees that the ciphertext has the same length as the
-- plaintext, and is the @plaintext `xor` stream k n@. Consequently
-- 'xor' can also be used to decrypt.
--
-- The 'stream' function, viewed as a function of the nonce for a
-- uniform random key, is designed to meet the standard notion of
-- unpredictability (\"PRF\"). For a formal definition see, e.g.,
-- Section 2.3 of Bellare, Kilian, and Rogaway, \"The security of the
-- cipher block chaining message authentication code,\" Journal of
-- Computer and System Sciences 61 (2000), 362399;
-- <http://www-cse.ucsd.edu/~mihir/papers/cbc.html>. This means that
-- an attacker cannot distinguish this function from a uniform random
-- function. Consequently, if a series of messages is encrypted by
-- 'xor' with /a different nonce for each message/, the ciphertexts
-- are indistinguishable from uniform random strings of the same
-- length.
--
-- 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.
--
-- Saltine does not make any promises regarding the resistance of
-- crypto_stream to \"related-key attacks.\" It is the caller's
-- responsibility to use proper key-derivation functions.
--
-- "Crypto.Saltine.Core.Stream" is @crypto_stream_xsalsa20@, a
-- particular cipher specified in \"Cryptography in NaCl\"
-- (<http://nacl.cr.yp.to/valid.html>), Section 7. This cipher is
-- conjectured to meet the standard notion of unpredictability.
--
-- This is version 2010.08.30 of the stream.html web page.
module Crypto.Saltine.Core.Stream (
Key, Nonce,
newKey, newNonce,
stream, xor
) where
import Crypto.Saltine.Internal.Stream ( c_stream
, c_stream_xor
, Key(..)
, Nonce(..)
)
import Crypto.Saltine.Internal.Util as U
import Data.ByteString (ByteString)
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.stream_keybytes
-- | Creates a random nonce of the correct size for 'stream' and
-- 'xor'.
newNonce :: IO Nonce
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
-- long as the 'Nonce' is not used more than once.
stream :: Key -> Nonce -> Int
-> ByteString
-- ^ Cryptographic stream
stream (Key key) (Nonce nonce) n =
snd . buildUnsafeByteString n $ \ps ->
constByteStrings [key, nonce] $ \[(pk, _), (pn, _)] ->
c_stream ps (fromIntegral n) pn pk
-- | Computes the exclusive-or between a message and a cryptographic
-- random stream indexed by the 'Key' and the 'Nonce'. This renders
-- the output indistinguishable from random noise so long as the
-- 'Nonce' is not used more than once. /Note:/ while this can be used
-- for encryption and decryption, it is /possible for an attacker to/
-- /manipulate the message in transit without detection/. USE AT YOUR
-- OWN RISK.
xor :: Key -> Nonce
-> ByteString
-- ^ Message
-> ByteString
-- ^ Ciphertext
xor (Key key) (Nonce nonce) msg =
snd . buildUnsafeByteString len $ \pc ->
constByteStrings [key, nonce, msg] $ \[(pk, _), (pn, _), (pm, _)] ->
c_stream_xor pc pm (fromIntegral len) pn pk
where len = S.length msg

View File

@ -0,0 +1,6 @@
module Crypto.Saltine.Core.Utils
( Crypto.Saltine.Internal.Util.randomByteString
, Crypto.Saltine.Internal.Util.bin2hex
) where
import qualified Crypto.Saltine.Internal.Util

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,193 @@
-- |
-- Module : Crypto.Saltine.Internal.ByteSizes
-- Copyright : (c) Joseph Abrahamson 2013
-- (c) Max Amanshauser 2021
-- License : MIT
--
-- Maintainer : max@lambdalifting.org
-- Stability : experimental
-- Portability : non-portable
--
module Crypto.Saltine.Internal.ByteSizes (
) where
-- Others
-- ------
-- src/libsodium/crypto_auth/hmacsha256/auth_hmacsha256_api.c
-- foreign import ccall "crypto_auth_hmacsha256_bytes"
-- c_crypto_auth_hmacsha256_bytes :: CSize
-- foreign import ccall "crypto_auth_hmacsha256_keybytes"
-- c_crypto_auth_hmacsha256_keybytes :: CSize
-- src/libsodium/crypto_auth/hmacsha512256/auth_hmacsha512256_api.c
-- foreign import ccall "crypto_auth_hmacsha512256_bytes"
-- c_crypto_auth_hmacsha512256_bytes :: CSize
-- foreign import ccall "crypto_auth_hmacsha512256_keybytes"
-- c_crypto_auth_hmacsha512256_keybytes :: CSize
-- src/libsodium/crypto_box/curve25519xsalsa20poly1305/box_curve25519xsalsa20poly1305_api.c
-- foreign import ccall "crypto_box_curve25519xsalsa20poly1305_publickeybytes"
-- c_crypto_box_curve25519xsalsa20poly1305_publickeybytes :: CSize
-- foreign import ccall "crypto_box_curve25519xsalsa20poly1305_secretkeybytes"
-- c_crypto_box_curve25519xsalsa20poly1305_secretkeybytes :: CSize
-- foreign import ccall "crypto_box_curve25519xsalsa20poly1305_beforenmbytes"
-- c_crypto_box_curve25519xsalsa20poly1305_beforenmbytes :: CSize
-- foreign import ccall "crypto_box_curve25519xsalsa20poly1305_noncebytes"
-- c_crypto_box_curve25519xsalsa20poly1305_noncebytes :: CSize
-- foreign import ccall "crypto_box_curve25519xsalsa20poly1305_zerobytes"
-- c_crypto_box_curve25519xsalsa20poly1305_zerobytes :: CSize
-- foreign import ccall "crypto_box_curve25519xsalsa20poly1305_boxzerobytes"
-- c_crypto_box_curve25519xsalsa20poly1305_boxzerobytes :: CSize
-- foreign import ccall "crypto_box_curve25519xsalsa20poly1305_macbytes"
-- c_crypto_box_curve25519xsalsa20poly1305_macbytes :: CSize
-- src/libsodium/crypto_core/hsalsa20/core_hsalsa20_api.c
-- foreign import ccall "crypto_core_hsalsa20_outputbytes"
-- c_crypto_core_hsalsa20_outputbytes :: CSize
-- foreign import ccall "crypto_core_hsalsa20_inputbytes"
-- c_crypto_core_hsalsa20_inputbytes :: CSize
-- foreign import ccall "crypto_core_hsalsa20_keybytes"
-- c_crypto_core_hsalsa20_keybytes :: CSize
-- foreign import ccall "crypto_core_hsalsa20_constbytes"
-- c_crypto_core_hsalsa20_constbytes :: CSize
-- src/libsodium/crypto_core/salsa20/core_salsa20_api.c
-- foreign import ccall "crypto_core_salsa20_outputbytes"
-- c_crypto_core_salsa20_outputbytes :: CSize
-- foreign import ccall "crypto_core_salsa20_inputbytes"
-- c_crypto_core_salsa20_inputbytes :: CSize
-- foreign import ccall "crypto_core_salsa20_keybytes"
-- c_crypto_core_salsa20_keybytes :: CSize
-- foreign import ccall "crypto_core_salsa20_constbytes"
-- c_crypto_core_salsa20_constbytes :: CSize
-- src/libsodium/crypto_core/salsa2012/core_salsa2012_api.c
-- foreign import ccall "crypto_core_salsa2012_outputbytes"
-- c_crypto_core_salsa2012_outputbytes :: CSize
-- foreign import ccall "crypto_core_salsa2012_inputbytes"
-- c_crypto_core_salsa2012_inputbytes :: CSize
-- foreign import ccall "crypto_core_salsa2012_keybytes"
-- c_crypto_core_salsa2012_keybytes :: CSize
-- foreign import ccall "crypto_core_salsa2012_constbytes"
-- c_crypto_core_salsa2012_constbytes :: CSize
-- src/libsodium/crypto_core/salsa208/core_salsa208_api.c
-- foreign import ccall "crypto_core_salsa208_outputbytes"
-- c_crypto_core_salsa208_outputbytes :: CSize
-- foreign import ccall "crypto_core_salsa208_inputbytes"
-- c_crypto_core_salsa208_inputbytes :: CSize
-- foreign import ccall "crypto_core_salsa208_keybytes"
-- c_crypto_core_salsa208_keybytes :: CSize
-- foreign import ccall "crypto_core_salsa208_constbytes"
-- c_crypto_core_salsa208_constbytes :: CSize
-- src/libsodium/crypto_generichash/blake2/generichash_blake2_api.c
-- foreign import ccall "crypto_generichash_blake2b_blockbytes"
-- c_crypto_generichash_blake2b_blockbytes :: CSize
-- src/libsodium/crypto_generichash/crypto_generichash.c
-- foreign import ccall "crypto_generichash_bytes"
-- c_crypto_generichash_bytes :: CSize
-- foreign import ccall "crypto_generichash_keybytes"
-- c_crypto_generichash_keybytes :: CSize
-- foreign import ccall "crypto_generichash_blockbytes"
-- c_crypto_generichash_blockbytes :: CSize
-- src/libsodium/crypto_hash/sha256/hash_sha256_api.c
-- foreign import ccall "crypto_hash_sha256_bytes"
-- c_crypto_hash_sha256_bytes :: CSize
-- src/libsodium/crypto_hash/sha512/hash_sha512_api.c
-- foreign import ccall "crypto_hash_sha512_bytes"
-- c_crypto_hash_sha512_bytes :: CSize
-- src/libsodium/crypto_hashblocks/sha256/hashblocks_sha256_api.c
-- foreign import ccall "crypto_hashblocks_sha256_statebytes"
-- c_crypto_hashblocks_sha256_statebytes :: CSize
-- foreign import ccall "crypto_hashblocks_sha256_blockbytes"
-- c_crypto_hashblocks_sha256_blockbytes :: CSize
-- src/libsodium/crypto_hashblocks/sha512/hashblocks_sha512_api.c
-- foreign import ccall "crypto_hashblocks_sha512_statebytes"
-- c_crypto_hashblocks_sha512_statebytes :: CSize
-- foreign import ccall "crypto_hashblocks_sha512_blockbytes"
-- c_crypto_hashblocks_sha512_blockbytes :: CSize
-- src/libsodium/crypto_onetimeauth/poly1305/onetimeauth_poly1305_api.c
-- foreign import ccall "crypto_onetimeauth_poly1305_bytes"
-- c_crypto_onetimeauth_poly1305_bytes :: CSize
-- foreign import ccall "crypto_onetimeauth_poly1305_keybytes"
-- c_crypto_onetimeauth_poly1305_keybytes :: CSize
-- src/libsodium/crypto_secretbox/xsalsa20poly1305/secretbox_xsalsa20poly1305_api.c
-- foreign import ccall "crypto_secretbox_xsalsa20poly1305_keybytes"
-- c_crypto_secretbox_xsalsa20poly1305_keybytes :: CSize
-- foreign import ccall "crypto_secretbox_xsalsa20poly1305_noncebytes"
-- c_crypto_secretbox_xsalsa20poly1305_noncebytes :: CSize
-- foreign import ccall "crypto_secretbox_xsalsa20poly1305_zerobytes"
-- c_crypto_secretbox_xsalsa20poly1305_zerobytes :: CSize
-- foreign import ccall "crypto_secretbox_xsalsa20poly1305_boxzerobytes"
-- c_crypto_secretbox_xsalsa20poly1305_boxzerobytes :: CSize
-- foreign import ccall "crypto_shorthash_siphash24_bytes"
-- c_crypto_shorthash_siphash24_bytes :: CSize
-- src/libsodium/crypto_sign/ed25519/sign_ed25519_api.c
-- foreign import ccall "crypto_sign_ed25519_bytes"
-- c_crypto_sign_ed25519_bytes :: CSize
-- foreign import ccall "crypto_sign_ed25519_publickeybytes"
-- c_crypto_sign_ed25519_publickeybytes :: CSize
-- foreign import ccall "crypto_sign_ed25519_secretkeybytes"
-- c_crypto_sign_ed25519_secretkeybytes :: CSize
-- src/libsodium/crypto_sign/edwards25519sha512batch/sign_edwards25519sha512batch_api.c
-- foreign import ccall "crypto_sign_edwards25519sha512batch_bytes"
-- c_crypto_sign_edwards25519sha512batch_bytes :: CSize
-- foreign import ccall "crypto_sign_edwards25519sha512batch_publickeybytes"
-- c_crypto_sign_edwards25519sha512batch_publickeybytes :: CSize
-- foreign import ccall "crypto_sign_edwards25519sha512batch_secretkeybytes"
-- c_crypto_sign_edwards25519sha512batch_secretkeybytes :: CSize
-- src/libsodium/crypto_stream/aes128ctr/stream_aes128ctr_api.c
-- foreign import ccall "crypto_stream_aes128ctr_keybytes"
-- c_crypto_stream_aes128ctr_keybytes :: CSize
-- foreign import ccall "crypto_stream_aes128ctr_noncebytes"
-- c_crypto_stream_aes128ctr_noncebytes :: CSize
-- foreign import ccall "crypto_stream_aes128ctr_beforenmbytes"
-- c_crypto_stream_aes128ctr_beforenmbytes :: CSize
-- src/libsodium/crypto_stream/aes256estream/stream_aes256estream_api.c
-- foreign import ccall "crypto_stream_aes256estream_keybytes"
-- c_crypto_stream_aes256estream_keybytes :: CSize
-- foreign import ccall "crypto_stream_aes256estream_noncebytes"
-- c_crypto_stream_aes256estream_noncebytes :: CSize
-- foreign import ccall "crypto_stream_aes256estream_beforenmbytes"
-- c_crypto_stream_aes256estream_beforenmbytes :: CSize
-- src/libsodium/crypto_stream/salsa2012/stream_salsa2012_api.c
-- foreign import ccall "crypto_stream_salsa2012_keybytes"
-- c_crypto_stream_salsa2012_keybytes :: CSize
-- foreign import ccall "crypto_stream_salsa2012_noncebytes"
-- c_crypto_stream_salsa2012_noncebytes :: CSize
-- src/libsodium/crypto_stream/salsa208/stream_salsa208_api.c
-- foreign import ccall "crypto_stream_salsa208_keybytes"
-- c_crypto_stream_salsa208_keybytes :: CSize
-- foreign import ccall "crypto_stream_salsa208_noncebytes"
-- c_crypto_stream_salsa208_noncebytes :: CSize
-- src/libsodium/crypto_stream/xsalsa20/stream_xsalsa20_api.c
-- foreign import ccall "crypto_stream_xsalsa20_keybytes"
-- c_crypto_stream_xsalsa20_keybytes :: CSize
-- foreign import ccall "crypto_stream_xsalsa20_noncebytes"
-- c_crypto_stream_xsalsa20_noncebytes :: CSize
-- src/libsodium/crypto_verify/16/verify_16_api.c
-- foreign import ccall "crypto_verify_16_bytes"
-- c_crypto_verify_16_bytes :: CSize
-- src/libsodium/crypto_verify/32/verify_32_api.c
-- foreign import ccall "crypto_verify_32_bytes"
-- c_crypto_verify_32_bytes :: CSize

View File

@ -0,0 +1,162 @@
{-# 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
, c_hash
, c_shorthash
, c_generichash
, 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 :: 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
-- 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
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,182 @@
{-# 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
, 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

View File

@ -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

View File

@ -0,0 +1,188 @@
{-# LANGUAGE BangPatterns #-}
module Crypto.Saltine.Internal.Util (
module Crypto.Saltine.Internal.Util
, withCString
, allocaBytes
)
where
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)@).
cycleSucc :: (Bounded a, Enum a, Eq a) => a -> (Bool, a)
cycleSucc a = (top, if top then minBound else succ a)
where top = a == maxBound
-- | Treats a 'ByteString' as a little endian bitstring and increments
-- it.
nudgeBS :: ByteString -> ByteString
nudgeBS i = fst $ S.unfoldrN (S.length i) go (True, i) where
go (toSucc, bs) = do
(hd, tl) <- S.uncons bs
let (top, hd') = cycleSucc hd
if toSucc
then return (hd', (top, tl))
else return (hd, (top && toSucc, tl))
-- | Computes the orbit of a endomorphism... in a very brute force
-- manner. Exists just for the below property.
--
-- prop> length . orbit nudgeBS . S.pack . replicate 0 == (256^)
orbit :: Eq a => (a -> a) -> a -> [a]
orbit f a0 = orbit' (f a0) where
orbit' a = if a == a0 then [a0] else a : orbit' (f a)
-- | 0-pad a 'ByteString'
pad :: Int -> ByteString -> ByteString
pad n = mappend (S.replicate n 0)
-- | Remove a 0-padding from a 'ByteString'
unpad :: Int -> ByteString -> ByteString
unpad = S.drop
-- | Converts a C-convention errno to an Either
handleErrno :: CInt -> (a -> Either String a)
handleErrno err a = case err of
0 -> Right a
-1 -> Left "failed"
n -> Left ("unexpected error code: " ++ show n)
unsafeDidSucceed :: IO CInt -> Bool
unsafeDidSucceed = go . unsafePerformIO
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 =
foldr (\v kk -> \k -> (unsafeUseAsCStringLen v) (\a -> kk (\as -> k (a:as)))) ($ [])
-- | Slightly safer cousin to 'buildUnsafeByteString' that remains in the
-- 'IO' monad.
buildUnsafeByteString' :: Int -> (Ptr CChar -> IO b) -> IO (b, ByteString)
buildUnsafeByteString' n k = do
ph <- mallocBytes n
bs <- unsafePackMallocCStringLen (ph, fromIntegral n)
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
-- it's difficult to predict the timing of the 'ByteString' creation.
buildUnsafeByteString :: Int -> (Ptr CChar -> IO b) -> (b, ByteString)
buildUnsafeByteString n = unsafePerformIO . buildUnsafeByteString' n
-- | Build a sized random 'ByteString' using Sodium's bindings to
-- @/dev/urandom@.
randomByteString :: Int -> IO ByteString
randomByteString n =
snd <$> buildUnsafeByteString' n (`c_randombytes_buf` fromIntegral n)
-- | To prevent a dependency on package 'errors'
hush :: Either s a -> Maybe a
hush = either (const Nothing) Just
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

View File

@ -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
]
]

View File

@ -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
]
]

View File

@ -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
]
]

View File

@ -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
]
]

21
tests/AuthProperties.hs Normal file
View File

@ -0,0 +1,21 @@
{-# LANGUAGE OverloadedStrings #-}
module AuthProperties (
testAuth
) where
import Util
import Crypto.Saltine.Core.Auth
import Test.Framework.Providers.QuickCheck2
import Test.Framework
testAuth :: Test
testAuth = buildTest $ do
k <- newKey
return $ testGroup "...Internal.Auth" [
testProperty "Authenticates message"
$ \(Message bs) -> verify k (auth k bs) bs == True
]

110
tests/BoxProperties.hs Normal file
View File

@ -0,0 +1,110 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE OverloadedLists #-}
module BoxProperties (
testBox
) where
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 (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 (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 (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 (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 (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
Keypair sk1 pk1 <- newKeypair
Keypair sk2 pk2 <- newKeypair
let ck_1for2 = beforeNM sk1 pk2
ck_2for1 = beforeNM sk2 pk1
return (ck_1for2 == ck_2for1)
-- | Ciphertext can be decrypted using combined keys
rightInverseAfterNMProp
:: CombinedKey -> CombinedKey -> Nonce -> Message -> Bool
rightInverseAfterNMProp ck_1for2 ck_2for1 n (Message bs) =
Just bs == boxOpenAfterNM ck_2for1 n (boxAfterNM ck_1for2 n bs)
-- | Perturbed ciphertext cannot be decrypted using combined keys
rightInverseFailureAfterNMProp1
:: CombinedKey -> CombinedKey -> Nonce -> Message -> Perturb -> Bool
rightInverseFailureAfterNMProp1 ck_1for2 ck_2for1 n (Message bs) p =
Nothing == boxOpenAfterNM ck_2for1 n (perturb (boxAfterNM ck_1for2 n bs) p)
testBox :: Test
testBox = buildTest $ do
kp1@(Keypair sk1 pk1) <- newKeypair
kp2@(Keypair sk2 pk2) <- newKeypair
let ck_1for2 = beforeNM sk1 pk2
ck_2for1 = beforeNM sk2 pk1
n1 <- newNonce
n2 <- newNonce
return $ testGroup "...Internal.Box" [
testGroup "Can decrypt ciphertext using..." [
testProperty "... public key/secret key"
$ rightInverseProp kp1 kp2 n1 ,
testProperty "... combined key"
$ rightInverseAfterNMProp ck_1for2 ck_2for1 n1
],
testGroup "Fail to verify ciphertext when..." [
testProperty "... not using proper secret key"
$ rightInverseFailureProp1 kp1 kp2 n1,
testProperty "... not actually sent to you"
$ rightInverseFailureProp2 kp1 kp2 n1,
testProperty "... ciphertext has been perturbed"
$ rightInverseFailureProp3 kp1 kp2 n1,
testProperty "... using the wrong nonce"
$ cannotDecryptNonceProp kp1 kp2 n1 n2,
testProperty "... using the wrong combined key"
$ rightInverseFailureAfterNMProp1 ck_1for2 ck_2for1 n1
],
testGroup "(properties)" [
testProperty "beforeNM is anti-symmetric" beforeNMCreateSecretKeyProp
]
]

46
tests/HashProperties.hs Normal file
View File

@ -0,0 +1,46 @@
{-# 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 "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 "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>\""

51
tests/Main.hs Normal file
View File

@ -0,0 +1,51 @@
{-# LANGUAGE OverloadedStrings #-}
module Main where
import SecretBoxProperties (testSecretBox)
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
runOpts :: RunnerOptions
runOpts = mempty { ropt_color_mode = Just ColorAlways
, ropt_test_options = Just testOpts
}
testOpts :: TestOptions
testOpts = mempty { topt_maximum_generated_tests = Just 20000 }
main :: IO ()
main = do
sodiumInit
flip defaultMainWithOpts runOpts [
testUtils,
testBox,
testSealedBox,
testSecretBox,
testAEADChaCha20,
testAEADIETF,
testAEADXChaCha20,
testAEADAES,
testStream,
testAuth,
testOneTimeAuth,
testSign,
testHash,
testScalarMult,
testPassword
]

View File

@ -0,0 +1,22 @@
{-# LANGUAGE OverloadedStrings #-}
module OneTimeAuthProperties (
testOneTimeAuth
) where
import Util
import Crypto.Saltine.Core.OneTimeAuth
import Test.Framework.Providers.QuickCheck2
import Test.Framework
testOneTimeAuth :: Test
testOneTimeAuth = buildTest $ do
k <- newKey
return $ testGroup "...Internal.Auth (one-time)" [
testProperty "Authenticates message"
$ \(Message bs) -> verify k (auth k bs) bs == True
]

View File

@ -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)
]

View File

@ -0,0 +1,74 @@
{-# LANGUAGE OverloadedStrings #-}
module ScalarMultProperties (
testScalarMult
) where
import Util
import Crypto.Saltine.Class
import Crypto.Saltine.Core.ScalarMult
import qualified Data.ByteString as S
import Data.Maybe (fromJust)
import Test.Framework.Providers.QuickCheck2
import Test.Framework
-- Test vectors extracted from "Cryptography in NaCl",
-- http://cr.yp.to/highspeed/naclcrypto-20090310.pdf
alicesk, bobsk :: Scalar
alicesk = fromJust . decode $ S.pack
[0x77,0x07,0x6d,0x0a,0x73,0x18,0xa5,0x7d
,0x3c,0x16,0xc1,0x72,0x51,0xb2,0x66,0x45
,0xdf,0x4c,0x2f,0x87,0xeb,0xc0,0x99,0x2a
,0xb1,0x77,0xfb,0xa5,0x1d,0xb9,0x2c,0x2a]
bobsk = fromJust . decode $ S.pack
[0x5d,0xab,0x08,0x7e,0x62,0x4a,0x8a,0x4b
,0x79,0xe1,0x7f,0x8b,0x83,0x80,0x0e,0xe6
,0x6f,0x3b,0xb1,0x29,0x26,0x18,0xb6,0xfd
,0x1c,0x2f,0x8b,0x27,0xff,0x88,0xe0,0xeb]
alicepk, bobpk, sharedsk :: GroupElement
alicepk = fromJust . decode $ S.pack
[0x85,0x20,0xf0,0x09,0x89,0x30,0xa7,0x54
,0x74,0x8b,0x7d,0xdc,0xb4,0x3e,0xf7,0x5a
,0x0d,0xbf,0x3a,0x0d,0x26,0x38,0x1a,0xf4
,0xeb,0xa4,0xa9,0x8e,0xaa,0x9b,0x4e,0x6a]
bobpk = fromJust . decode $ S.pack
[0xde,0x9e,0xdb,0x7d,0x7b,0x7d,0xc1,0xb4
,0xd3,0x5b,0x61,0xc2,0xec,0xe4,0x35,0x37
,0x3f,0x83,0x43,0xc8,0x5b,0x78,0x67,0x4d
,0xad,0xfc,0x7e,0x14,0x6f,0x88,0x2b,0x4f]
sharedsk = fromJust . decode $ S.pack
[0x4a,0x5d,0x9d,0x5b,0xa4,0xce,0x2d,0xe1
,0x72,0x8e,0x3b,0xf4,0x80,0x35,0x0f,0x25
,0xe0,0x7e,0x21,0xc9,0x47,0xd1,0x9e,0x33
,0x76,0xf0,0x9b,0x3c,0x1e,0x16,0x17,0x42]
testScalarMult :: Test
testScalarMult = buildTest $
return $ testGroup "...Internal.ScalarMult" [
testProperty "mult a (multBase a) /= multBase a"
$ \(ByteString32 a') ->
let Just a = decode a'
in mult a (multBase a) /= multBase a,
testProperty "mult a (multBase b) == mult b (multBase a)"
$ \(ByteString32 a') (ByteString32 b') ->
let Just a = decode a'
Just b = decode b'
in mult a (multBase b) == mult b (multBase a),
testProperty "matches test vector for alice"
$ multBase alicesk == alicepk,
testProperty "matches test vector for bob"
$ multBase bobsk == bobpk,
testProperty "matches test vector for shared secret from alice's view"
$ mult alicesk bobpk == sharedsk,
testProperty "matches test vector for shared secret from bob's view"
$ mult bobsk alicepk == sharedsk
]

View File

@ -0,0 +1,71 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE OverloadedLists #-}
module SealedBoxProperties (
testSealedBox
) where
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 (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 (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 (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 (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 (Keypair sk1 pk1) (Message bs) p = do
enc <- boxSeal pk1 bs
return (Nothing == boxSealOpen pk1 sk1 (perturb enc p))
testSealedBox :: Test
testSealedBox = buildTest $ do
kp <- newKeypair
return $ testGroup "... SealedBox" [
testGroup "Can decrypt ciphertext using..." [
testProperty "... public key/secret key"
$ ioProperty . rightInverseProp kp
],
testGroup "Fail to verify ciphertext when..." [
testProperty "... not using proper secret key"
$ ioProperty . uncurry (rightInverseFailureProp1 kp),
testProperty "... not using proper public key"
$ ioProperty . uncurry (rightInverseFailureProp2 kp),
testProperty "... not actually sent to you"
$ ioProperty . uncurry (rightInverseFailureProp3 kp),
testProperty "... ciphertext has been perturbed"
$ ioProperty . uncurry (rightInverseFailureProp4 kp)
]
]

View File

@ -0,0 +1,116 @@
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module SecretBoxProperties (
testSecretBox
) where
import Util
import Crypto.Saltine.Core.SecretBox
import Crypto.Saltine.Class
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, (==>))
import Test.QuickCheck.Arbitrary
instance Arbitrary Nonce where
arbitrary =
do bs <- S.pack <$> vector Internal.secretbox_noncebytes
pure $ fromJust (decode bs)
instance Arbitrary Key where
arbitrary =
do bs <- S.pack <$> vector Internal.secretbox_keybytes
pure $ fromJust (decode bs)
-- | Ciphertext can be decrypted
rightInverseProp :: Key -> Nonce -> Message -> Bool
rightInverseProp k n (Message bs) =
Just bs == secretboxOpen k n (secretbox k n bs)
-- | Detached ciphertext/tag can be decrypted
rightInverseDetachedProp :: Key -> Nonce -> Message -> Bool
rightInverseDetachedProp k n (Message bs) =
Just bs == uncurry (secretboxOpenDetached k n) (secretboxDetached k n bs)
-- | Ciphertext cannot be decrypted if the ciphertext is perturbed
rightInverseFailureProp :: Key -> Nonce -> Message -> Perturb -> Property
rightInverseFailureProp k n (Message bs) p =
let ct = secretbox k n bs
fakeCT = perturb ct p
in ct /= fakeCT ==> Nothing == secretboxOpen k n fakeCT
-- | Ciphertext cannot be decrypted if the tag is perturbed
rightInverseTagFailureProp :: Key -> Nonce -> Message -> Message -> Property
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
rightInverseFailureDetachedProp :: Key -> Nonce -> Message -> Perturb -> Property
rightInverseFailureDetachedProp k n (Message bs) p =
let (tag,ct) = secretboxDetached k n bs
fakeCT = perturb ct p
in fakeCT /= ct ==> Nothing == secretboxOpenDetached k n tag fakeCT
-- | Ciphertext cannot be decrypted with a different key
cannotDecryptKeyProp :: Key -> Key -> Nonce -> Message -> Property
cannotDecryptKeyProp k1 k2 n (Message bs) =
k1 /= k2 ==> Nothing == secretboxOpen k2 n (secretbox k1 n bs)
-- | Ciphertext cannot be decrypted with a different key
cannotDecryptKeyDetachedProp :: Key -> Key -> Nonce -> Message -> Property
cannotDecryptKeyDetachedProp k1 k2 n (Message bs) =
k1 /= k2 ==> Nothing == uncurry (secretboxOpenDetached k2 n) (secretboxDetached k1 n bs)
-- | Ciphertext cannot be decrypted with a different nonce
cannotDecryptNonceProp :: Key -> Nonce -> Nonce -> Message -> Property
cannotDecryptNonceProp k n1 n2 (Message bs) =
n1 /= n2 ==> Nothing == secretboxOpen k n2 (secretbox k n1 bs)
-- | Ciphertext cannot be decrypted with a different nonce
cannotDecryptNonceDetachedProp :: Key -> Nonce -> Nonce -> Message -> Property
cannotDecryptNonceDetachedProp k n1 n2 (Message bs) =
n1 /= n2 ==> Nothing == uncurry (secretboxOpenDetached k n2) (secretboxDetached k n1 bs)
testSecretBox :: Test
testSecretBox = buildTest $ do
return $ testGroup "...Internal.SecretBox" [
testProperty "Can decrypt ciphertext"
$ rightInverseProp,
testProperty "Can decrypt ciphertext (detached)"
$ rightInverseDetachedProp,
testGroup "Cannot decrypt ciphertext when..." [
testProperty "... ciphertext is perturbed"
$ rightInverseFailureProp,
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
]
]

43
tests/SignProperties.hs Normal file
View File

@ -0,0 +1,43 @@
{-# LANGUAGE OverloadedStrings #-}
module SignProperties (
testSign
) where
import Util
import Crypto.Saltine.Core.Sign
import Crypto.Saltine.Internal.Sign
import qualified Data.ByteString as S
import Test.Framework.Providers.QuickCheck2
import Test.Framework
import Test.QuickCheck
testSign :: Test
testSign = buildTest $ do
kp1 <- newKeypair
let sk1 = secretKey kp1
let pk1 = publicKey kp1
kp2 <- newKeypair
let pk2 = publicKey kp2
return $ testGroup "...Internal.Sign" [
testProperty "Verifies signed message"
$ \(Message bs) -> signOpen pk1 (sign sk1 bs) == Just bs,
testProperty "Verifies signed message w/ detached signature"
$ \(Message bs) -> signVerifyDetached pk1 (signDetached sk1 bs) bs,
testProperty "Signed message longer than message"
$ \(Message bs) -> S.length (sign sk1 bs) >= S.length bs,
testProperty "Rejects message with mismatched key"
$ \(Message bs) -> not (S.null bs) ==>
signOpen pk2 (sign sk1 bs) == Nothing,
testProperty "Rejects message with mismatched key w/ detached signature"
$ \(Message bs) -> not (S.null bs) ==>
not (signVerifyDetached pk2 (signDetached sk1 bs) bs)
]

32
tests/StreamProperties.hs Normal file
View File

@ -0,0 +1,32 @@
{-# LANGUAGE OverloadedStrings #-}
module StreamProperties (
testStream
) where
import Util
import Crypto.Saltine.Core.Stream
import qualified Data.ByteString as S
import Test.Framework.Providers.QuickCheck2
import Test.Framework
import Test.QuickCheck
testStream :: Test
testStream = buildTest $ do
k <- newKey
n <- newNonce
return $ testGroup "...Internal.Stream" [
testProperty "Stream is apropriately sized"
$ \len -> (len > 0 && len < 200)
==> S.length (stream k n len) == len,
testProperty "xor munges input"
$ \(Message bs) -> not (S.null bs)
==> xor k n bs /= bs,
testProperty "xor is involutive"
$ \(Message bs) -> xor k n (xor k n bs) == bs
]

55
tests/Util.hs Normal file
View File

@ -0,0 +1,55 @@
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Util where
import Crypto.Saltine.Class
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 Test.QuickCheck
import GHC.Exts (IsList(..))
instance IsEncoding S.ByteString where
encode x = x
decode x = Just x
perturb :: IsEncoding a => a -> Perturb -> a
perturb a (Perturb p) =
let bytes = encode a
len = S.length bytes
plen = length p
fullP = p <> replicate (len - plen) 0
newBytes = S.pack $ zipWith xor fullP (S.unpack bytes)
in case decode newBytes of
Nothing -> error "Invalid use of perturb on picky encoding."
Just x -> x
newtype Perturb = Perturb [Word8]
deriving (Show,Semigroup,Monoid)
instance IsList Perturb where
type Item Perturb = Word8
fromList = Perturb
toList (Perturb x) = x
instance Arbitrary Perturb where
arbitrary =
do bs <- arbitrary
if all (==0) bs
then pure (Perturb (1:bs))
else pure (Perturb bs)
newtype ByteString32 = ByteString32 S.ByteString deriving (Eq,Show)
instance Arbitrary ByteString32 where
arbitrary = ByteString32 . S.pack <$> replicateM 32 arbitrary
newtype Message = Message S.ByteString deriving (Show)
instance Arbitrary Message where
arbitrary = Message . S.pack <$> arbitrary

28
tests/UtilProperties.hs Normal file
View File

@ -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
]