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