├── .editorconfig ├── .git-blame-ignore-revs ├── .githooks └── pre-commit ├── .github ├── check.sh ├── dependabot.yml ├── mergify.yml └── workflows │ ├── ci.yaml │ ├── ghc-nightly.yaml │ └── linting.yml ├── .gitignore ├── .hlint.yaml ├── CONTRIBUTING.md ├── Makefile ├── README.md ├── cabal.homebrew.project ├── cabal.pkg-config.project ├── cabal.project ├── fourmolu.yaml ├── hie.yaml ├── libsodium-bindings ├── CHANGELOG.md ├── LICENSE ├── README.md ├── libsodium-bindings.cabal └── src │ └── LibSodium │ ├── Bindings.hs │ └── Bindings │ ├── AEAD.hs │ ├── Comparison.hs │ ├── CryptoAuth.hs │ ├── CryptoBox.hs │ ├── CryptoSign.hs │ ├── GenericHashing.hs │ ├── KeyDerivation.hs │ ├── KeyExchange.hs │ ├── Main.hs │ ├── PasswordHashing.hs │ ├── Random.hs │ ├── SHA2.hs │ ├── Scrypt.hs │ ├── SealedBoxes.hs │ ├── SecretStream.hs │ ├── Secretbox.hs │ ├── SecureMemory.hs │ ├── ShortHashing.hs │ ├── Utils.hs │ └── XChaCha20.hs └── sel ├── CHANGELOG.md ├── LICENSE ├── README.md ├── sel.cabal ├── src ├── Sel.hs └── Sel │ ├── HMAC.hs │ ├── HMAC │ ├── SHA256.hs │ ├── SHA512.hs │ └── SHA512_256.hs │ ├── Hashing.hs │ ├── Hashing │ ├── Password.hs │ ├── SHA256.hs │ ├── SHA512.hs │ └── Short.hs │ ├── Internal.hs │ ├── Internal │ ├── Scoped.hs │ ├── Scoped │ │ └── Foreign.hs │ └── Sodium.hs │ ├── PublicKey │ ├── Cipher.hs │ ├── Seal.hs │ └── Signature.hs │ ├── Scrypt.hs │ └── SecretKey │ ├── Authentication.hs │ ├── Cipher.hs │ └── Stream.hs └── test ├── Main.hs ├── Test ├── HMAC.hs ├── Hashing.hs ├── Hashing │ ├── Password.hs │ ├── SHA2.hs │ └── Short.hs ├── PublicKey │ ├── Cipher.hs │ ├── Seal.hs │ └── Signature.hs ├── Scrypt.hs └── SecretKey │ ├── Authentication.hs │ ├── Cipher.hs │ └── Stream.hs ├── TestUtils.hs ├── package-api-9.6.6.txt └── package-api-9.8.2.txt /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*.hs] 6 | indent_style = space 7 | indent_size = 2 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | charset = utf-8 11 | end_of_line = lf 12 | 13 | [LICENSE] 14 | insert_final_newline = false 15 | 16 | [Makefile] 17 | indent_style = tab 18 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # fourmolu 0.17.0.0 and cabal-gild formatting 2 | eab5847b465349bdda613f00dec7f31d83bb1f6d 3 | -------------------------------------------------------------------------------- /.githooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if which fourmolu > /dev/null ; then 4 | fourmolu --mode check sel libsodium-bindings 5 | else 6 | echo "Fourmolu not found, aborting." 7 | exit 1 8 | fi 9 | 10 | if which hlint > /dev/null ; then 11 | hlint src libsodium-bindings 12 | else 13 | echo "HLint not found, aborting." 14 | exit 1 15 | fi 16 | -------------------------------------------------------------------------------- /.github/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ormolu_bin=$1 4 | 5 | cabal_fmt_bin=$2 6 | 7 | find src -name "*.hs" | while read -r f; do 8 | if ! "${ormolu_bin}" -m 'check' "$f" 1> /dev/null 2> /dev/null ; then 9 | echo "$f is not formatted, aborting." 10 | exit 1 11 | fi 12 | done 13 | 14 | if ! "${ormolu_bin}" -m 'check' "Setup.hs" 1> /dev/null 2> /dev/null ; then 15 | echo "Setup.hs is not formatted, aborting." 16 | exit 1 17 | fi 18 | 19 | find . -maxdepth 1 -name "*.cabal" | while read -r f; do 20 | if ! "${cabal_fmt_bin}" -c "$f" ; then 21 | echo "$f is not formatted, aborting." 22 | exit 1 23 | fi 24 | done 25 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Set update schedule for GitHub Actions 2 | 3 | version: 2 4 | updates: 5 | 6 | - package-ecosystem: "github-actions" 7 | directory: "/" 8 | schedule: 9 | # Check for updates to GitHub Actions every week 10 | interval: "weekly" 11 | -------------------------------------------------------------------------------- /.github/mergify.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | # rebase+merge strategy 3 | - name: refactored queue action rule 4 | conditions: [] 5 | actions: 6 | queue: 7 | queue_rules: 8 | - name: default 9 | queue_conditions: 10 | - label=merge me 11 | - '#approved-reviews-by>=1' 12 | - label=squash+merge me 13 | - '#approved-reviews-by>=1' 14 | conditions: [] 15 | merge_method: squash 16 | update_method: merge 17 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | # Trigger the workflow on push or pull request, but only for the main branch 4 | on: 5 | pull_request: 6 | push: 7 | branches: ["main"] 8 | 9 | jobs: 10 | generateMatrix: 11 | name: "Generate matrix from cabal" 12 | runs-on: ubuntu-latest 13 | outputs: 14 | matrix: ${{ steps.set-matrix.outputs.matrix }} 15 | steps: 16 | - name: Extract the tested GHC versions 17 | id: set-matrix 18 | uses: kleidukos/get-tested@0.1.7.1 19 | with: 20 | cabal-file: libsodium-bindings/libsodium-bindings.cabal 21 | ubuntu-version: "latest" 22 | macos-version: "latest" 23 | version: 0.1.7.0 24 | tests: 25 | name: ${{ matrix.ghc }} on ${{ matrix.os }} 26 | needs: generateMatrix 27 | runs-on: ${{ matrix.os }} 28 | strategy: 29 | matrix: ${{ fromJSON(needs.generateMatrix.outputs.matrix) }} 30 | steps: 31 | - name: Checkout base repo 32 | uses: actions/checkout@v4 33 | 34 | - name: Set up Haskell 35 | id: setup-haskell 36 | uses: haskell-actions/setup@v2 37 | with: 38 | ghc-version: ${{ matrix.ghc }} 39 | cabal-version: 'latest' 40 | 41 | - name: Install libsodium-dev on ubuntu 42 | if: ${{ matrix.os == 'ubuntu-latest' }} 43 | run: | 44 | sudo apt install libsodium-dev 45 | 46 | - name: Freeze 47 | run: cabal freeze 48 | 49 | - name: Cache 50 | uses: actions/cache@v4 51 | with: 52 | path: ${{ steps.setup-haskell.outputs.cabal-store }} 53 | key: ${{ runner.os }}-ghc-${{ matrix.ghc }}-cabal-${{ hashFiles('**/plan.json') }} 54 | restore-keys: ${{ runner.os }}-ghc-${{ matrix.ghc }}- 55 | 56 | - name: Build libsodium-bindings with homebrew 57 | if: ${{ matrix.os == 'macos-latest' }} 58 | run: | 59 | cabal build -v2 --project-file=cabal.homebrew.project libsodium-bindings 60 | 61 | - name: Build sel with homebrew 62 | if: ${{ matrix.os == 'macos-latest' }} 63 | run: | 64 | cabal build --project-file=cabal.homebrew.project -v2 sel 65 | cabal test --project-file=cabal.homebrew.project sel 66 | 67 | - name: Build libsodium-bindings with pkg-config 68 | if: ${{ matrix.os == 'ubuntu-latest' }} 69 | run: | 70 | cabal build --project-file=cabal.pkg-config.project -v2 libsodium-bindings 71 | 72 | - name: Build sel with pkg-config 73 | if: ${{ matrix.os == 'ubuntu-latest' }} 74 | run: | 75 | cabal build --project-file=cabal.pkg-config.project -v2 sel 76 | cabal test --project-file=cabal.pkg-config.project sel 77 | 78 | api-stabiity: 79 | name: API Stability 80 | needs: tests 81 | runs-on: ubuntu-latest 82 | steps: 83 | - name: Checkout base repo 84 | uses: actions/checkout@v4 85 | 86 | - name: Install libsodium-dev on ubuntu 87 | run: | 88 | sudo apt install libsodium-dev 89 | 90 | - name: Set up Haskell 91 | id: setup-haskell 92 | uses: haskell-actions/setup@v2 93 | with: 94 | ghc-version: '9.8.2' 95 | cabal-version: 'latest' 96 | 97 | - name: Freeze 98 | run: cabal freeze --project-file=cabal.pkg-config.project 99 | 100 | - name: Cache 101 | uses: actions/cache@v4 102 | with: 103 | path: ${{ steps.setup-haskell.outputs.cabal-store }} 104 | key: ${{ runner.os }}-ghc-${{ steps.setup-haskell.outputs.ghc-version }}-cabal-${{ hashFiles('**/plan.json') }} 105 | restore-keys: ${{ runner.os }}-ghc-${{ steps.setup-haskell.outputs.ghc-version }}- 106 | 107 | - name: Build 108 | run: cabal build --project-file=cabal.pkg-config.project --write-ghc-environment-files=always sel 109 | 110 | - name: Diff the expected and actual package APIs 111 | uses: kleidukos/diff-package-api@v0.1.0.1 112 | with: 113 | package-name: sel 114 | expected-interface: sel/test/package-api-${{ steps.setup-haskell.outputs.ghc-version }}.txt 115 | ghc: ${{ steps.setup-haskell.outputs.ghc-version }} 116 | version: 0.1.0.1 # This is the version of the print-api tool 117 | -------------------------------------------------------------------------------- /.github/workflows/ghc-nightly.yaml: -------------------------------------------------------------------------------- 1 | name: GHC Nightly 2 | 3 | # Trigger the workflow on push or pull request, but only for the main branch 4 | on: 5 | pull_request: 6 | push: 7 | branches: ["main"] 8 | 9 | jobs: 10 | tests: 11 | name: Tests on GHC Nightly 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - uses: haskell/ghcup-setup@v1 17 | 18 | - name: Setup GHC Nightly 19 | run: | 20 | ghcup config add-release-channel https://ghc.gitlab.haskell.org/ghcup-metadata/ghcup-nightlies-2025-0.0.7.yaml 21 | ghcup install ghc latest-nightly 22 | ghcup install cabal -f -u https://github.com/haskell/cabal/releases/download/cabal-head/cabal-head-Linux-x86_64.tar.gz head 23 | ghcup set cabal head 24 | cabal update 25 | 26 | - name: Install libsodium-dev on ubuntu 27 | run: | 28 | sudo apt install libsodium-dev 29 | 30 | - name: Build libsodium-bindings with pkg-config 31 | run: | 32 | cabal build --allow-newer --project-file=cabal.pkg-config.project -v2 libsodium-bindings 33 | 34 | - name: Build sel with pkg-config 35 | run: | 36 | cabal build --allow-newer --project-file=cabal.pkg-config.project -v2 sel 37 | cabal test --project-file=cabal.pkg-config.project sel 38 | -------------------------------------------------------------------------------- /.github/workflows/linting.yml: -------------------------------------------------------------------------------- 1 | name: Linting 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: ["main"] 7 | 8 | jobs: 9 | fourmolu: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - uses: haskell-actions/run-fourmolu@v11 15 | with: 16 | version: "0.17.0.0" 17 | pattern: | 18 | sel/**/*.hs 19 | libsodium-bindings/**/*.hs 20 | 21 | hlint: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v4 25 | 26 | - name: 'Set up HLint' 27 | uses: haskell-actions/hlint-setup@v2 28 | with: 29 | version: '3.8' 30 | 31 | - name: 'Run HLint' 32 | uses: haskell-actions/hlint-run@v2 33 | with: 34 | path: '["sel", "libsodium-bindings"]' 35 | fail-on: warning 36 | 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .hie/ 2 | .hspec-failures 3 | < 4 | Session.vim 5 | cabal.project.local* 6 | dist* 7 | hlint.log 8 | stdout.log 9 | tags 10 | tags.mtime 11 | .nvimrc 12 | 13 | # nix 14 | result* 15 | .pre-commit-config.yaml 16 | 17 | # direnv 18 | .direnv 19 | 20 | # Libsodium 21 | *.bc 22 | *.dSYM 23 | *.done 24 | *.final 25 | *.gcda 26 | *.gcno 27 | *.i 28 | *.la 29 | *.lo 30 | *.log 31 | *.mem 32 | *.nexe 33 | *.o 34 | *.plist 35 | *.scan 36 | *.sdf 37 | *.status 38 | *.tar.* 39 | *.wasm 40 | *.wast 41 | *~ 42 | .DS_Store 43 | .deps 44 | .dirstamp 45 | .done 46 | .libs 47 | /bin/ 48 | /obj/ 49 | Build 50 | INSTALL 51 | Makefile 52 | Makefile.in 53 | Vagrantfile 54 | aclocal.m4 55 | android-toolchain 56 | android-toolchain-* 57 | autom4te.cache 58 | build 59 | confdefs.h 60 | configure.lineno 61 | coverage.info 62 | depcomp 63 | libsodium-*.tar.bz2 64 | libsodium-*.tar.gz 65 | libsodium-*.vcproj 66 | libsodium-*.vcproj.filters 67 | libsodium-*.vcxproj 68 | libsodium-*.vcxproj.filters 69 | libsodium-android-* 70 | libsodium-ios 71 | libsodium-js 72 | libsodium-js-* 73 | libsodium-nativeclient 74 | libsodium-nativeclient-* 75 | libsodium-osx 76 | libsodium-uninstalled.pc 77 | libsodium-wasm32-wasi 78 | libsodium-win32 79 | libsodium-win64 80 | libsodium.pc 81 | libtool 82 | m4/argz.m4 83 | m4/libtool.m4 84 | m4/ltoptions.m4 85 | m4/ltsugar.m4 86 | m4/ltversion.m4 87 | m4/lt~obsolete.m4 88 | man/*.html 89 | man/Makefile.in 90 | src/libsodium/*.def 91 | src/libsodium/include/sodium/version.h 92 | stamp-* 93 | test-driver 94 | test/default/*.asm.js 95 | test/default/*.res 96 | test/default/*.trs 97 | test/default/aead_aes256gcm 98 | test/default/aead_aes256gcm2 99 | test/default/aead_chacha20poly1305 100 | test/default/aead_chacha20poly13052 101 | test/default/aead_xchacha20poly1305 102 | test/default/auth 103 | test/default/auth2 104 | test/default/auth3 105 | test/default/auth5 106 | test/default/auth6 107 | test/default/auth7 108 | test/default/box 109 | test/default/box2 110 | test/default/box7 111 | test/default/box8 112 | test/default/box_easy 113 | test/default/box_easy2 114 | test/default/box_seal 115 | test/default/box_seed 116 | test/default/browser 117 | test/default/chacha20 118 | test/default/codecs 119 | test/default/core1 120 | test/default/core2 121 | test/default/core3 122 | test/default/core4 123 | test/default/core5 124 | test/default/core6 125 | test/default/core_ed25519 126 | test/default/core_ristretto255 127 | test/default/ed25519_convert 128 | test/default/generichash 129 | test/default/generichash2 130 | test/default/generichash3 131 | test/default/hash 132 | test/default/hash3 133 | test/default/kdf 134 | test/default/keygen 135 | test/default/kx 136 | test/default/metamorphic 137 | test/default/misuse 138 | test/default/onetimeauth 139 | test/default/onetimeauth2 140 | test/default/onetimeauth7 141 | test/default/pwhash_argon2i 142 | test/default/pwhash_argon2id 143 | test/default/pwhash_scrypt 144 | test/default/pwhash_scrypt_ll 145 | test/default/randombytes 146 | test/default/scalarmult 147 | test/default/scalarmult2 148 | test/default/scalarmult5 149 | test/default/scalarmult6 150 | test/default/scalarmult7 151 | test/default/scalarmult8 152 | test/default/scalarmult_ed25519 153 | test/default/scalarmult_ristretto255 154 | test/default/secretbox 155 | test/default/secretbox2 156 | test/default/secretbox7 157 | test/default/secretbox8 158 | test/default/secretbox_easy 159 | test/default/secretbox_easy2 160 | test/default/secretstream 161 | test/default/shorthash 162 | test/default/sign 163 | test/default/siphashx24 164 | test/default/sodium_core 165 | test/default/sodium_utils 166 | test/default/sodium_utils2 167 | test/default/sodium_utils3 168 | test/default/sodium_version 169 | test/default/stream 170 | test/default/stream2 171 | test/default/stream3 172 | test/default/stream4 173 | test/default/verify1 174 | test/default/xchacha20 175 | test/js.done 176 | testing 177 | -------------------------------------------------------------------------------- /.hlint.yaml: -------------------------------------------------------------------------------- 1 | - ignore: {name: "Eta reduce"} 2 | - ignore: {name: "Use tuple-section" } 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Thank you for your contribution to `sel`! While there is no 2 | Contributor License Agreement (CLA) to sign, we _do_ need you to read the 3 | following instructions before you contribute. 4 | 5 | ## Code of Conduct 6 | 7 | We need you to read, acknowledge, and abide by our [Code of Conduct][CoC]. 8 | 9 | ### Pull Requests 10 | 11 | When making a PR, ensure that you have a Github issue that explains the context for your changes. 12 | 13 | ## Code Style 14 | 15 | ### C FFI 16 | 17 | * The [CApiFFI convention](https://www.haskell.org/ghc/blog/20210709-capi-usage.html) must be used at all times. 18 | * The datatypes from [`Foreign`](https://hackage.haskell.org/package/base/docs/Foreign.html) must be used when 19 | getting results from C, like `CInt` in favour of `Int`. 20 | Example: 21 | - ❌ `foreign export ccall sodium_init :: IO Int` 22 | 23 | - ✅ `foreign import capi "sodium.h sodium_init" c_sodium_init :: IO CInt` 24 | 25 | ### Formatting and linting 26 | 27 | We have a git hook in place to ensure the following formatting and linting tools 28 | are being used: 29 | 30 | * All Haskell source files are formatted with 31 | [`fourmolu`](https://flora.pm/packages/@hackage/fourmolu/0.17.0.0) v0.17.0.0; 32 | * All Haskell source files are linted with 33 | [`hlint` v3.8](https://flora.pm/packages/@hackage/hlint/3.8), as per the `.hlint.yaml` 34 | configuration file. 35 | * The Cabal file is formatted with 36 | [`cabal-gild` v1.5.0.1](https://flora.pm/packages/@hackage/cabal-gild/1.5.0.1) 37 | 38 | To ensure that you are using the git hook, run the following, once: 39 | 40 | ``` 41 | git config core.hooksPath .githooks 42 | ``` 43 | 44 | You can also use the provided `Makefile` by running `make init`. 45 | 46 | ### Questions 47 | 48 | Open a thread in the [Questions][Questions board] discussion board. That way, 49 | you can get help from everyone in the community. 50 | 51 | ### Issues & Bugs 52 | 53 | Open an [issue][Ticket] and tell us what you can about your problem. 54 | 55 | [CoC]: https://github.com/haskell-cryptography/governance/blob/master/CODE_OF_CONDUCT.md 56 | [Ticket]: https://github.com/haskell-cryptography/libsodium-bindings/issues/new 57 | [Questions board]: https://github.com/haskell-cryptography/libsodium-bindings/discussions/categories/q-a 58 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | init: ## Set up git hooks properly - needs calling once 2 | git config core.hooksPath .githooks 3 | 4 | deps: ## Install the dependencies of the backend 5 | @cabal build all --only-dependencies 6 | 7 | build: ## Build the project in fast mode 8 | @cabal build all 9 | 10 | test: ## Build and run the test suite 11 | @cabal test all 12 | 13 | clean: ## Remove compilation artifacts 14 | @cabal clean 15 | 16 | bindings: ## Start a REPL for the `libsodium-bindings` package 17 | @cabal repl --repl-options -fobject-code libsodium-bindings 18 | 19 | sel: ## Start a REPL for the `sel` package 20 | @cabal repl --repl-options -fobject-code sel 21 | 22 | lint: ## Run the code linter (HLint) 23 | @find sel libsodium-bindings -name "*.hs" | xargs -P $(PROCS) -I {} hlint --refactor-options="-i" --refactor {} 24 | 25 | style: ## Run the code formatter (fourmolu, cabal-fmt) 26 | @cabal-gild --mode=format --io=libsodium-bindings/libsodium-bindings.cabal 27 | @cabal-gild --mode=format --io=sel/sel.cabal 28 | @fourmolu -q --mode inplace sel libsodium-bindings 29 | 30 | help: ## Display this help message 31 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.* ?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' 32 | 33 | PROCS := $(shell nproc) 34 | 35 | .PHONY: all $(MAKECMDGOALS) 36 | 37 | .DEFAULT_GOAL := help 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sodium Bindings [![made with Haskell](https://img.shields.io/badge/Made%20in-Haskell-%235e5086?logo=haskell&style=flat-square)](https://haskell.org) 2 | 3 | The Haskell Cryptography Group presents its suite of libsodium packages: 4 | 5 | | Package | Status | 6 | |--------------------------|------------------------| 7 | | [sel][sel] | [![sel-badge]][sel-ci] | 8 | | [libsodium‑bindings][lb] | [![lb-badge]][lb-ci] | 9 | 10 | ## Comparison with other libraries 11 | 12 | | Name | Nature | Dependencies | GHC Support 13 | |----------------------|--------------------------------------------|------------------------------------------------------------------------------|-------------------- 14 | | `libsodium‑bindings` | Low-level FFI bindings | `base` | Starts with 9.2.8 15 | | `sel` | High-level Haskell interface | `base`, `base16`, `bytestring`, `text` `text-display`, `libsodium‑bindings` | Starts with 9.2.8 16 | | `saltine` | Both FFI bindings and high-level interface | `base`, `bytestring` `deepseq`, `text`, `hashable`, `profunctors` | Starts with 8.0.2 17 | | `libsodium` | Low-level FFI bindings | `base` | 8.6.5 to 8.10.1 18 | | `crypto‑sodium` | High-level Haskell interface | `base`, `bytestring`, `random`, `cereal`, `libsodium`, `memory`, | Unclear 19 | 20 | | Name | FFI Convention | Library Discovery 21 | |----------------------|--------------------------------|------------------- 22 | | `libsodium‑bindings` | Recommended `capi` convention | `pkg-config`, `homebrew` (macOS-only), cabal‑native 23 | | `saltine` | Legacy `ccall` convention | `pkg-config`, cabal-native 24 | | `libsodium` | Legacy `ccall` convention | `pkg-config` 25 | 26 | [sel]: https://github.com/haskell-cryptography/libsodium-bindings/blob/main/sel/README.md 27 | [sel-badge]: https://github.com/haskell-cryptography/libsodium-bindings/actions/workflows/sel.yml/badge.svg 28 | [sel-ci]: https://github.com/haskell-cryptography/libsodium-bindings/actions/workflows/sel.yml?query=branch%3Amain 29 | 30 | [lb]: https://github.com/haskell-cryptography/libsodium-bindings/blob/main/libsodium-bindings/README.md 31 | [lb-badge]: https://github.com/haskell-cryptography/libsodium-bindings/actions/workflows/libsodium-bindings.yml/badge.svg 32 | [lb-ci]: https://github.com/haskell-cryptography/libsodium-bindings/actions/workflows/libsodium-bindings.yml?query=branch%3Amain 33 | -------------------------------------------------------------------------------- /cabal.homebrew.project: -------------------------------------------------------------------------------- 1 | import: ./cabal.project 2 | 3 | package libsodium-bindings 4 | flags: +homebrew -pkg-config 5 | -------------------------------------------------------------------------------- /cabal.pkg-config.project: -------------------------------------------------------------------------------- 1 | import: ./cabal.project 2 | 3 | package libsodium-bindings 4 | flags: -homebrew +pkg-config 5 | 6 | -------------------------------------------------------------------------------- /cabal.project: -------------------------------------------------------------------------------- 1 | packages: 2 | ./libsodium-bindings/libsodium-bindings.cabal 3 | ./sel/sel.cabal 4 | 5 | package libsodium-bindings 6 | ghc-options: -Werror 7 | 8 | package sel 9 | ghc-options: -Werror 10 | 11 | package * 12 | ghc-options: -haddock 13 | 14 | test-show-details: direct 15 | tests: True 16 | documentation: True 17 | -------------------------------------------------------------------------------- /fourmolu.yaml: -------------------------------------------------------------------------------- 1 | indentation: 2 2 | comma-style: leading # for lists, tuples etc. - can also be 'leading' 3 | import-export-style: leading 4 | record-brace-space: false # rec {x = 1} vs. rec{x = 1} 5 | indent-wheres: true # 'false' means save space by only half-indenting the 'where' keyword 6 | respectful: true # don't be too opinionated about newlines etc. 7 | haddock-style: single-line # '--' vs. '{-' 8 | newlines-between-decls: 1 # number of newlines between top-level declarations 9 | fixities: [] 10 | function-arrows: leading 11 | single-constraint-parens: never 12 | import-grouping: by-scope 13 | sort-constraints: true 14 | sort-derived-classes: true 15 | sort-deriving-clauses: true 16 | -------------------------------------------------------------------------------- /hie.yaml: -------------------------------------------------------------------------------- 1 | cradle: 2 | cabal: 3 | - path: "libsodium-bindings/src" 4 | component: "lib:libsodium-bindings" 5 | 6 | - path: "sel/src" 7 | component: "lib:sel" 8 | 9 | - path: "sel/test" 10 | component: "sel:test:sel-tests" 11 | -------------------------------------------------------------------------------- /libsodium-bindings/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 0.0.3.0 4 | 5 | * Add constant-time pointer comparison [#171](https://github.com/haskell-cryptography/libsodium-bindings/pull/171) 6 | 7 | ## 0.0.2.0 8 | 9 | * Add secret key Stream [#144](https://github.com/haskell-cryptography/libsodium-bindings/pull/144) 10 | 11 | ## 0.0.1.1 (2024-01-12) 12 | 13 | * Fix typo in the identifiers of LibSodium.Bindings.Scrypt ([#135](https://github.com/haskell-cryptography/libsodium-bindings/pull/135)) 14 | 15 | ## 0.0.1.0 (2023-11-19) 16 | -------------------------------------------------------------------------------- /libsodium-bindings/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Hécate Moonlight and contributors 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /libsodium-bindings/README.md: -------------------------------------------------------------------------------- 1 | # libsodium-bindings [![CI](https://github.com/haskell-cryptography/libsodium-bindings/actions/workflows/ci.yaml/badge.svg)](https://github.com/haskell-cryptography/libsodium-bindings/actions/workflows/ci.yaml) [![made with Haskell](https://img.shields.io/badge/Made%20in-Haskell-%235e5086?logo=haskell&style=flat-square)](https://haskell.org) 2 | 3 | `libsodium-bindings` exposes a set of FFI bindings from the `libsodium` library, version 1.0.18 and above. 4 | It has no other dependency than `base`, and the documentation is strong enough to stand alone. 5 | 6 | See [LibSodium.Bindings](https://hackage-content.haskell.org/package/libsodium-bindings/candidate/docs/LibSodium-Bindings.html) for a list of available functions. 7 | -------------------------------------------------------------------------------- /libsodium-bindings/libsodium-bindings.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 3.0 2 | name: libsodium-bindings 3 | version: 0.0.3.0 4 | category: Cryptography 5 | synopsis: FFI bindings to libsodium 6 | description: 7 | This library embeds FFI bindings to the stable version of libsodium 1.0.18. 8 | The interface exposed by this library is kept close to the C library. 9 | 10 | homepage: https://github.com/haskell-cryptography/libsodium-bindings 11 | bug-reports: 12 | https://github.com/haskell-cryptography/libsodium-bindings/issues 13 | 14 | author: Hécate Moonlight, Koz Ross 15 | maintainer: The Haskell Cryptography contributors 16 | license: BSD-3-Clause 17 | build-type: Simple 18 | tested-with: 19 | GHC ==9.2.8 || ==9.4.8 || ==9.6.6 || ==9.8.2 || ==9.10.1 || ==9.12.1 20 | 21 | extra-source-files: 22 | LICENSE 23 | README.md 24 | 25 | extra-doc-files: CHANGELOG.md 26 | 27 | flag pkg-config 28 | description: Use pkg-config to find Libsodium (macOS and linux only). 29 | default: False 30 | manual: True 31 | 32 | flag homebrew 33 | description: Use Homebrew version of Libsodium (macOS only). 34 | default: False 35 | manual: True 36 | 37 | source-repository head 38 | type: git 39 | location: https://github.com/haskell-cryptography/libsodium-bindings 40 | subdir: libsodium-bindings 41 | 42 | common common 43 | build-depends: base >=4.14 && <5 44 | ghc-options: 45 | -Wall 46 | -Wcompat 47 | -Widentities 48 | -Wincomplete-record-updates 49 | -Wincomplete-uni-patterns 50 | -Wpartial-fields 51 | -Wredundant-constraints 52 | -fhide-source-paths 53 | -Wno-unused-do-bind 54 | 55 | if (os(osx) && flag(homebrew)) 56 | include-dirs: 57 | /opt/homebrew/include 58 | /opt/local/include 59 | 60 | extra-lib-dirs: 61 | /opt/homebrew/lib 62 | /user/local/opt/libsodium/lib 63 | 64 | if flag(pkg-config) 65 | pkgconfig-depends: libsodium >=1.0.18 66 | else 67 | extra-libraries: sodium 68 | 69 | default-language: Haskell2010 70 | 71 | common common-rts-options 72 | ghc-options: 73 | -rtsopts 74 | -threaded 75 | -with-rtsopts=-N 76 | 77 | library 78 | import: common 79 | hs-source-dirs: src 80 | -- cabal-fmt: expand src/ 81 | exposed-modules: 82 | LibSodium.Bindings 83 | LibSodium.Bindings.AEAD 84 | LibSodium.Bindings.Comparison 85 | LibSodium.Bindings.CryptoAuth 86 | LibSodium.Bindings.CryptoBox 87 | LibSodium.Bindings.CryptoSign 88 | LibSodium.Bindings.GenericHashing 89 | LibSodium.Bindings.KeyDerivation 90 | LibSodium.Bindings.KeyExchange 91 | LibSodium.Bindings.Main 92 | LibSodium.Bindings.PasswordHashing 93 | LibSodium.Bindings.Random 94 | LibSodium.Bindings.SHA2 95 | LibSodium.Bindings.Scrypt 96 | LibSodium.Bindings.SealedBoxes 97 | LibSodium.Bindings.SecretStream 98 | LibSodium.Bindings.Secretbox 99 | LibSodium.Bindings.SecureMemory 100 | LibSodium.Bindings.ShortHashing 101 | LibSodium.Bindings.Utils 102 | LibSodium.Bindings.XChaCha20 103 | -------------------------------------------------------------------------------- /libsodium-bindings/src/LibSodium/Bindings/AEAD.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CApiFFI #-} 2 | 3 | -- | 4 | -- 5 | -- Module: LibSodium.Bindings.AEAD 6 | -- Description: Bindings to AEAD constructions with XChaCha20-Poly1305-IETF. 7 | -- Copyright: (C) Hécate Moonlight 2023 8 | -- License: BSD-3-Clause 9 | -- Maintainer: The Haskell Cryptography Group 10 | -- Stability: Stable 11 | -- Portability: GHC only 12 | module LibSodium.Bindings.AEAD 13 | ( -- * Introduction 14 | -- $introduction 15 | 16 | -- * Operations 17 | cryptoAEADXChaCha20Poly1305IETFEncrypt 18 | , cryptoAEADXChaCha20Poly1305IETFDecrypt 19 | , cryptoAEADXChaCha20Poly1305IETFEncryptDetached 20 | , cryptoAEADXChaCha20Poly1305IETFDecryptDetached 21 | 22 | -- * Constants 23 | , cryptoAEADXChaCha20Poly1305IETFKeyBytes 24 | , cryptoAEADXChaCha20Polt1305IETFPubBytes 25 | , cryptoAEADXChaCha20Poly1305IETFABytes 26 | ) 27 | where 28 | 29 | import Foreign.C.Types (CInt (CInt), CSize (CSize), CUChar, CULLong (CULLong)) 30 | import Foreign.Ptr (Ptr) 31 | 32 | -- $introduction 33 | -- 34 | -- With @XChaCha20-Poly1305-IETF@, you can encrypt a message witha key and a nonce to keept it 35 | -- confidential, as well as compute an authentication tag to make sure that the message 36 | -- has not been tampered with. 37 | -- 38 | -- A typical use case for additional data is to authenticate protocol-specific metadata 39 | -- about the message, such as its length and encoding. 40 | -- 41 | -- For a deeper dive into the limitations of the implementation, please refer to the manual: 42 | -- https://doc.libsodium.org/secret-key_cryptography/aead#limitations 43 | 44 | -- | This function encrypts a message, and then appends the authentication tag 45 | -- to the encrypted message. 46 | -- 47 | -- /See:/ [crypto_aead_xchacha20poly1305_ietf_encrypt()](https://doc.libsodium.org/secret-key_cryptography/aead/chacha20-poly1305/xchacha20-poly1305_construction#combined-mode) 48 | -- 49 | -- @since 0.0.1.0 50 | foreign import capi "sodium.h crypto_aead_xchacha20poly1305_ietf_encrypt" 51 | cryptoAEADXChaCha20Poly1305IETFEncrypt 52 | :: Ptr CUChar 53 | -- ^ Output buffer. Contains the encrypted message, authentication tag, and non-confidential additional data. 54 | -> Ptr CULLong 55 | -- ^ Size of computed output. Should be message length plus 'cryptoAEADXChaCha20Poly1305IETFABytes'. 56 | -- If set to 'Foreign.Ptr.nullPtr', then no bytes will be written to this buffer. 57 | -> Ptr CUChar 58 | -- ^ Message to be encrypted. 59 | -> CULLong 60 | -- ^ Message length. 61 | -> Ptr CUChar 62 | -- ^ Non-confidential additional data. Can be null with additional data length of 0 if 63 | -- no additional data is required. 64 | -> CULLong 65 | -- ^ Additional data length. 66 | -> Ptr CUChar 67 | -- ^ @nsec@, a parameter not used in this function. Should always be 'Foreign.Ptr.nullPtr'. 68 | -> Ptr CUChar 69 | -- ^ Public nonce of size 'cryptoAEADXChaCha20Polt1305IETFPubBytes'. 70 | -- Should never be reused with the same key. Nonces can be generated using 'LibSodium.Bindings.Random.randombytesBuf'. 71 | -> Ptr CUChar 72 | -- ^ Secret key of size 'cryptoAEADXChaCha20Poly1305IETFKeyBytes'. 73 | -> IO CInt 74 | -- ^ Returns -1 on failure, 0 on success. 75 | 76 | -- | This function verifies that an encrypted ciphertext includes a valid tag. 77 | -- 78 | -- /See:/ [crypto_aead_xchacha20poly1305_ietf_decrypt()](https://doc.libsodium.org/secret-key_cryptography/aead/chacha20-poly1305/xchacha20-poly1305_construction#combined-mode) 79 | -- 80 | -- @since 0.0.1.0 81 | foreign import capi "sodium.h crypto_aead_xchacha20poly1305_ietf_decrypt" 82 | cryptoAEADXChaCha20Poly1305IETFDecrypt 83 | :: Ptr CUChar 84 | -- ^ Output buffer. At most the cipher text length minus 'cryptoAEADXChaCha20Poly1305IETFABytes' will be put into this. 85 | -> Ptr CULLong 86 | -- ^ Size of computed output. Should be message length plus 'cryptoAEADXChaCha20Poly1305IETFABytes'. 87 | -- If set to 'Foreign.Ptr.nullPtr', then no bytes will be written to this buffer. 88 | -> Ptr CUChar 89 | -- ^ @nsec@, a parameter not used in this function. Should always be 'Foreign.Ptr.nullPtr'. 90 | -> Ptr CUChar 91 | -- ^ Ciphertext to decrypt. 92 | -> CULLong 93 | -- ^ Ciphertext length. 94 | -> Ptr CUChar 95 | -- ^ Non-confidential additional data. Can be null with additional data length of 0 if 96 | -- no additional data is required. 97 | -> CULLong 98 | -- ^ Additional data length. 99 | -> Ptr CUChar 100 | -- ^ Public nonce of size 'cryptoAEADXChaCha20Polt1305IETFPubBytes'. 101 | -- Should never be reused with the same key. Nonces can be generated using 'LibSodium.Bindings.Random.randombytesBuf'. 102 | -> Ptr CUChar 103 | -- ^ Secret key of size 'cryptoAEADXChaCha20Poly1305IETFKeyBytes'. 104 | -> IO CInt 105 | -- ^ Returns -1 on failure, 0 on success. 106 | 107 | -- | This is the "detached" version of the encryption function. 108 | -- The encrypted message and authentication tag are output to different buffers 109 | -- instead of the tag being appended to the encrypted message. 110 | -- 111 | -- /See:/ [crypto_aead_xchacha20poly1305_ietf_encrypt_detached()](https://doc.libsodium.org/secret-key_cryptography/aead/chacha20-poly1305/xchacha20-poly1305_construction#detached-mode) 112 | -- 113 | -- @since 0.0.1.0 114 | foreign import capi "sodium.h crypto_aead_xchacha20poly1305_ietf_encrypt_detached" 115 | cryptoAEADXChaCha20Poly1305IETFEncryptDetached 116 | :: Ptr CUChar 117 | -- ^ Output buffer. Contains the encrypted message with length equal to the message. 118 | -> Ptr CUChar 119 | -- ^ The authentication tag. Has length 'cryptoAEADXChaCha20Poly1305IETFABytes'. 120 | -> Ptr CULLong 121 | -- ^ Length of the authentication tag buffer. 122 | -> Ptr CUChar 123 | -- ^ Message to be encrypted. 124 | -> CULLong 125 | -- ^ Length of input message. 126 | -> Ptr CUChar 127 | -- ^ Additional, non-confidential data. 128 | -> CULLong 129 | -- ^ Length of the additional, non-confidential data. 130 | -> Ptr CUChar 131 | -- ^ Not used in this particular construction, should always be 'Foreign.Ptr.nullPtr'. 132 | -> Ptr CUChar 133 | -- ^ Public nonce of size 'cryptoAEADXChaCha20Polt1305IETFPubBytes'. 134 | -- Should never be reused with the same key. Nonces can be generated using 'LibSodium.Bindings.Random.randombytesBuf'. 135 | -> Ptr CUChar 136 | -- ^ Secret key of size 'cryptoAEADXChaCha20Poly1305IETFKeyBytes'. 137 | -> IO CInt 138 | -- ^ Returns -1 on failure, 0 on success. 139 | 140 | -- | This is the "detached" version of the decryption function. 141 | -- Verifies that the authentication tag is valid for the ciphertext, key, nonce, 142 | -- and additional data. 143 | -- 144 | -- /See:/ [crypto_aead_xchacha20poly1305_ietf_decrypt_detached()](https://doc.libsodium.org/secret-key_cryptography/aead/chacha20-poly1305/xchacha20-poly1305_construction#detached-mode) 145 | -- 146 | -- @since 0.0.1.0 147 | foreign import capi "sodium.h crypto_aead_xchacha20poly1305_ietf_decrypt_detached" 148 | cryptoAEADXChaCha20Poly1305IETFDecryptDetached 149 | :: Ptr CUChar 150 | -- ^ If the tag is valid, the ciphertext is decrypted and put into this buffer. 151 | -> Ptr CUChar 152 | -- ^ Not used in this particular construction, should always be 'Foreign.Ptr.nullPtr'. 153 | -> Ptr CUChar 154 | -- ^ Ciphertext to be decrypted. 155 | -> CULLong 156 | -- ^ Length of the ciphertext. 157 | -> Ptr CUChar 158 | -- ^ The authentication tag. Has length 'cryptoAEADXChaCha20Poly1305IETFABytes'. 159 | -> Ptr CUChar 160 | -- ^ Additional, non-confidential data. 161 | -> CULLong 162 | -- ^ Length of the additional, non-confidential data. 163 | -> Ptr CUChar 164 | -- ^ Public nonce of size 'cryptoAEADXChaCha20Polt1305IETFPubBytes'. 165 | -- Should never be reused with the same key. Nonces can be generated using 'LibSodium.Bindings.Random.randombytesBuf'. 166 | -> Ptr CUChar 167 | -- ^ Secret key of size 'cryptoAEADXChaCha20Poly1305IETFKeyBytes'. 168 | -> IO CInt 169 | -- ^ Returns 0 on success, -1 if tag is not valid. 170 | 171 | -- == Constants. 172 | 173 | -- | Recommended length of a key for this construction. 174 | -- 175 | -- /See:/ [crypto_aead_xchacha20poly1305_ietf_KEYBYTES](https://doc.libsodium.org/secret-key_cryptography/aead/chacha20-poly1305/xchacha20-poly1305_construction#constants) 176 | -- 177 | -- @since 0.0.1.0 178 | foreign import capi "sodium.h value crypto_aead_xchacha20poly1305_ietf_KEYBYTES" 179 | cryptoAEADXChaCha20Poly1305IETFKeyBytes :: CSize 180 | 181 | -- | Recommended length of a nonce for this construction. 182 | -- 183 | -- /See:/ [crypto_aead_xchacha20poly1305_ietf_NPUBBYTES](https://doc.libsodium.org/secret-key_cryptography/aead/chacha20-poly1305/xchacha20-poly1305_construction#constants) 184 | -- 185 | -- @since 0.0.1.0 186 | foreign import capi "sodium.h value crypto_aead_xchacha20poly1305_ietf_NPUBBYTES" 187 | cryptoAEADXChaCha20Polt1305IETFPubBytes :: CSize 188 | 189 | -- | Recommended length for the authentication tag. 190 | -- 191 | -- /See:/ [crypto_aead_xchacha20poly1305_ietf_ABYTES](https://doc.libsodium.org/secret-key_cryptography/aead/chacha20-poly1305/xchacha20-poly1305_construction#constants) 192 | -- 193 | -- @since 0.0.1.0 194 | foreign import capi "sodium.h value crypto_aead_xchacha20poly1305_ietf_ABYTES" 195 | cryptoAEADXChaCha20Poly1305IETFABytes :: CSize 196 | -------------------------------------------------------------------------------- /libsodium-bindings/src/LibSodium/Bindings/Comparison.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CApiFFI #-} 2 | {-# LANGUAGE Trustworthy #-} 3 | 4 | -- | Module: LibSodium.Bindings.Comparison 5 | -- Description: Helper functions for constant-time comparison 6 | -- Copyright: (C) Koz Ross 2022, Jack Henahan 2025 7 | -- License: BSD-3-Clause 8 | -- Maintainer: koz.ross@retro-freedom.nz 9 | -- Stability: Stable 10 | -- Portability: GHC only 11 | -- 12 | -- Secure comparison functions, designed to run in constant time for a given 13 | -- input length. 14 | module LibSodium.Bindings.Comparison 15 | ( sodiumMemcmp 16 | , sodiumCompare 17 | , sodiumIsZero 18 | ) 19 | where 20 | 21 | import Foreign.C.Types (CInt (CInt), CSize (CSize), CUChar) 22 | import Foreign.Ptr (Ptr) 23 | 24 | -- | Compares the given amount of bytes at the given locations for equality. 25 | -- Constant-time for any given length. 26 | -- 27 | -- /See:/ [sodium_memcmp()](https://doc.libsodium.org/helpers#constant-time-test-for-equality) 28 | -- 29 | -- @since 0.0.1.0 30 | foreign import capi "sodium.h sodium_memcmp" 31 | sodiumMemcmp 32 | :: Ptr CUChar 33 | -- ^ First location with data to compare 34 | -> Ptr CUChar 35 | -- ^ Second location with data to compare 36 | -> CSize 37 | -- ^ How many bytes to compare 38 | -> CInt 39 | -- ^ 0 if all bytes match, -1 otherwise 40 | 41 | -- | Lexicographically compares the requested bytes of the given 42 | -- pointers in constant time. 43 | -- 44 | -- /See:/ [sodium_compare()](https://libsodium.gitbook.io/doc/helpers#comparing-large-numbers) 45 | -- 46 | -- @since 0.0.3.0 47 | foreign import capi "sodium.h sodium_compare" 48 | sodiumCompare 49 | :: Ptr CUChar 50 | -- ^ First pointer data (lhs) 51 | -> Ptr CUChar 52 | -- ^ Second pointer data (rhs) 53 | -> CSize 54 | -- ^ Bytes to compare 55 | -> IO CInt 56 | -- ^ Comparison result 57 | -- lhs == rhs -> 0 58 | -- lhs < rhs -> -1 59 | -- lhs > rhs -> 1 60 | 61 | -- | Checks if the given number of bytes at the given location are all equal to 62 | -- zero. Constant-time for any given length. 63 | -- 64 | -- /See:/ [sodium_is_zero()](https://doc.libsodium.org/helpers#testing-for-all-zeros) 65 | -- 66 | -- @since 0.0.1.0 67 | foreign import capi "sodium.h sodium_is_zero" 68 | sodiumIsZero 69 | :: Ptr CUChar 70 | -- ^ Location with data to check 71 | -> CSize 72 | -- ^ How many bytes to check 73 | -> CInt 74 | -- ^ 1 if all the bytes were zeroes, 0 otherwise 75 | -------------------------------------------------------------------------------- /libsodium-bindings/src/LibSodium/Bindings/CryptoAuth.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CApiFFI #-} 2 | {-# LANGUAGE ScopedTypeVariables #-} 3 | 4 | -- | 5 | -- Module: LibSodium.Bindings.CryptoAuth 6 | -- Description: Direct bindings to the secret key authentication primitives backed by HMAC-SHA512-256 7 | -- Copyright: (C) Hécate Moonlight 8 | -- License: BSD-3-Clause 9 | -- Maintainer: The Haskell Cryptography Group 10 | -- Stability: Stable 11 | -- Portability: GHC only 12 | module LibSodium.Bindings.CryptoAuth 13 | ( -- * Introduction 14 | -- $introduction 15 | 16 | -- * Usage 17 | -- $usage 18 | 19 | -- * Functions 20 | cryptoAuth 21 | , cryptoAuthVerify 22 | , cryptoAuthKeygen 23 | 24 | -- * Constants 25 | , cryptoAuthKeyBytes 26 | , cryptoAuthBytes 27 | ) where 28 | 29 | import Foreign 30 | import Foreign.C 31 | 32 | -- $introduction 33 | -- Compute an authentication tag for a message and a secret key, 34 | -- and verify that a given tag is valid for a given message and a key. 35 | -- 36 | -- The function computing the tag is deterministic: the same (message, key) 37 | -- tuple will always produce the same output. 38 | -- However, even if the message is public, knowing the key is required in order to be 39 | -- able to compute a valid tag. 40 | -- Therefore, the key __should remain confidential__. The tag, however, can be public. 41 | -- 42 | -- The operations of this module are backed by the HMAC-SHA512-256 algorithm. 43 | 44 | -- $usage 45 | -- 46 | -- A typical use case is: 47 | -- 48 | -- * @A@ prepares a message, adds an authentication tag, sends it to @B@ 49 | -- * @A@ doesn't store the message 50 | -- * Later on, @B@ sends the message and the authentication tag to @A@ 51 | -- * @A@ uses the authentication tag to verify that it created this message. 52 | -- 53 | -- This operation does not encrypt the message. 54 | -- It only computes and verifies an authentication tag. 55 | 56 | -- | Compute a tag for the provided message and key. 57 | -- 58 | -- /See:/ [crypto_auth()](https://doc.libsodium.org/secret-key_cryptography/secret-key_authentication#usage) 59 | -- 60 | -- @since 0.0.1.0 61 | foreign import capi "sodium.h crypto_auth" 62 | cryptoAuth 63 | :: Ptr CUChar 64 | -- ^ Buffer that will hold the computed authenticated tag, of size 'cryptoAuthBytes' 65 | -> Ptr CUChar 66 | -- ^ Buffer that holds the input message 67 | -> CULLong 68 | -- ^ Length of the message 69 | -> Ptr CUChar 70 | -- ^ Buffer that holds the secret key of size 'cryptoAuthKeyBytes' 71 | -> IO CInt 72 | 73 | -- | Verify that the tag is valid for the provided message and secret key. 74 | -- 75 | -- /See:/ [crypto_auth_verify()](https://doc.libsodium.org/secret-key_cryptography/secret-key_authentication#usage) 76 | -- 77 | -- @since 0.0.1.0 78 | foreign import capi "sodium.h crypto_auth_verify" 79 | cryptoAuthVerify 80 | :: Ptr CUChar 81 | -- ^ Buffer that holds the tag 82 | -> Ptr CUChar 83 | -- ^ Buffer that holds the message 84 | -> CULLong 85 | -- ^ Length of the message 86 | -> Ptr CUChar 87 | -- ^ Buffer that holds the secret key of size 'cryptoAuthKeyBytes' 88 | -> IO CInt 89 | -- ^ Returns -1 if the verification fails, and 0 if it passes. 90 | 91 | -- | Create a random secret key of size 'cryptoAuthKeyBytes' 92 | -- 93 | -- It is equivalent to calling 'LibSodium.Bindings.Random.randombytesBuf' but 94 | -- improves code clarity and can prevent misuse by ensuring that the provided 95 | -- key length is always be correct. 96 | -- 97 | -- /See:/ [crypto_auth_keygen()](https://doc.libsodium.org/secret-key_cryptography/secret-key_authentication#usage) 98 | -- 99 | -- @since 0.0.1.0 100 | foreign import capi "sodium.h crypto_auth_keygen" 101 | cryptoAuthKeygen 102 | :: Ptr CUChar 103 | -- ^ Buffer that holds the secret key of size 'cryptoAuthKeyBytes' 104 | -> IO () 105 | 106 | -- === Constants === 107 | 108 | -- | Size of the secret key 109 | -- 110 | -- /See:/ [crypto_auth_KEYBYTES](https://doc.libsodium.org/secret-key_cryptography/secret-key_authentication#constants) 111 | -- 112 | -- @since 0.0.1.0 113 | foreign import capi "sodium.h value crypto_auth_KEYBYTES" 114 | cryptoAuthKeyBytes :: CSize 115 | 116 | -- | Size of the tag 117 | -- 118 | -- /See:/ [crypto_auth_BYTES](https://doc.libsodium.org/secret-key_cryptography/secret-key_authentication#constants) 119 | -- 120 | -- @since 0.0.1.0 121 | foreign import capi "sodium.h value crypto_auth_BYTES" 122 | cryptoAuthBytes :: CSize 123 | -------------------------------------------------------------------------------- /libsodium-bindings/src/LibSodium/Bindings/GenericHashing.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CApiFFI #-} 2 | {-# LANGUAGE Trustworthy #-} 3 | 4 | -- | 5 | -- 6 | -- Module: LibSodium.Bindings.GenericHashing 7 | -- Description: Direct bindings to the Blake2 hashing primitives of Libsodium. 8 | -- Copyright: (C) Hécate Moonlight 2022 9 | -- License: BSD-3-Clause 10 | -- Maintainer: The Haskell Cryptography Group 11 | -- Stability: Stable 12 | -- Portability: GHC only 13 | module LibSodium.Bindings.GenericHashing 14 | ( -- * Introduction 15 | -- $introduction 16 | 17 | -- * Operations 18 | CryptoGenericHashState 19 | , cryptoGenericHash 20 | , cryptoGenericHashKeyGen 21 | , withGenericHashState 22 | , withGenericHashStateOfSize 23 | , cryptoGenericHashInit 24 | , cryptoGenericHashUpdate 25 | , cryptoGenericHashFinal 26 | 27 | -- * Constants 28 | , cryptoGenericHashBytes 29 | , cryptoGenericHashBytesMin 30 | , cryptoGenericHashBytesMax 31 | , cryptoGenericHashKeyBytes 32 | , cryptoGenericHashKeyBytesMin 33 | , cryptoGenericHashKeyBytesMax 34 | , cryptoGenericHashStateBytes 35 | ) 36 | where 37 | 38 | import Foreign (Ptr, allocaBytes) 39 | import Foreign.C (CInt (CInt), CSize (CSize), CUChar, CULLong (CULLong)) 40 | 41 | -- $introduction 42 | -- This API computes a fixed-length fingerprint for an arbitrarily long message. 43 | -- It is backed by the [BLAKE2b](https://blake2.net) algorithm. 44 | -- 45 | -- Sample use cases: 46 | -- 47 | -- * File integrity checking 48 | -- * Creating unique identifiers to index arbitrarily long data 49 | -- 50 | -- ⚠️ Do not use this API module to hash passwords! 51 | -- 52 | -- Whenever there is a @'Ptr' 'CryptoGenericHashState'@, it must point to enough memory 53 | -- to hold the hash state. 54 | -- This is at least 'cryptoGenericHashBytesMin', at most 55 | -- 'cryptoGenericHashBytesMax', and should typically be 'cryptoGenericHashBytes'. 56 | -- It is the caller's responsibility to ensure that this holds. 57 | 58 | -- | Opaque tag representing the hash state struct @crypto_generichash_state@ used by the C API. 59 | -- 60 | -- To use a 'CryptoGenericHashState', use 'withGenericHashState'. 61 | -- 62 | -- @since 0.0.1.0 63 | data CryptoGenericHashState 64 | 65 | -- | This function allocates a 'CryptoGenericHashState' of size 'cryptoGenericHashBytes'. 66 | -- If you want more control over the size of the hash state, use 'withGenericHashStateOfSize'. 67 | -- 68 | -- ⚠️ Do not leak the 'CryptoGenericHashState' outside of the lambda, 69 | -- otherwise you will point at deallocated memory! 70 | -- 71 | -- @since 0.0.1.0 72 | withGenericHashState :: (Ptr CryptoGenericHashState -> IO a) -> IO a 73 | withGenericHashState action = withGenericHashStateOfSize cryptoGenericHashBytes action 74 | 75 | -- | This function allocates a 'CryptoGenericHashState' of the desired size. 76 | -- 77 | -- Use the following constants as parameter to this function: 78 | -- 79 | -- * 'cryptoGenericHashBytesMin' (16U) 80 | -- * 'cryptoGenericHashBytes' (32U) 81 | -- * 'cryptoGenericHashBytesMax' (64U) 82 | -- 83 | -- @since 0.0.1.0 84 | withGenericHashStateOfSize :: CSize -> (Ptr CryptoGenericHashState -> IO a) -> IO a 85 | withGenericHashStateOfSize size action = allocaBytes (fromIntegral size) action 86 | 87 | -- | Put a fingerprint of the message (the @in@ parameter) of length @inlen@ into 88 | -- the @out@ buffer. 89 | -- The minimum recommended output size (@outlen@) is 'cryptoGenericHashBytes'. 90 | -- However, for specific use cases, the size can be any value between 'cryptoGenericHashBytesMin' (included) 91 | -- and 'cryptoGenericHashBytesMax' (included). 92 | -- 93 | -- The @key@ parameter can be 'Foreign.nullPtr' and keylen can be 0. In this case, a message will always have the same fingerprint 94 | -- But a key can also be specified. A message will always have the same fingerprint for a given key, but different 95 | -- keys used to hash the same message are very likely to produce distinct fingerprints. 96 | -- In particular, the key can be used to make sure that different applications generate different fingerprints even 97 | -- if they process the same data. 98 | -- 99 | -- The recommended key size is 'cryptoGenericHashKeyBytes' bytes. 100 | -- 101 | -- However, the key size can be any value between 0 (included) and 'cryptoGenericHashKeyBytesMax' (included). 102 | -- If the key is meant to be secret, the recommended minimum length is 'cryptoGenericHashKeyBytesMin'. 103 | -- 104 | -- /See:/ [crypto_generichash()](https://doc.libsodium.org/hashing/generic_hashing#usage) 105 | -- 106 | -- @since 0.0.1.0 107 | foreign import capi "sodium.h crypto_generichash" 108 | cryptoGenericHash 109 | :: Ptr CUChar 110 | -- ^ @out@ parameter. 111 | -> CSize 112 | -- ^ @outlen@ parameter. 113 | -> Ptr CUChar 114 | -- ^ @in@ parameter. 115 | -> CULLong 116 | -- ^ @inlen@ parameter. 117 | -> Ptr CUChar 118 | -- ^ @key@ parameter. 119 | -> CSize 120 | -- ^ @keylen@ parameter. 121 | -> IO CInt 122 | -- ^ Returns 0 on success, -1 on error. 123 | 124 | -- | Initialise a hash state with a key of a specified length, and 125 | -- produce an output with the specified length in bytes. 126 | -- 127 | -- The @'Ptr' 'CUChar'@ argument must point to enough memory to hold a key, 128 | -- which must also be initialised. 129 | -- This is at least 'cryptoGenericHashKeyBytesMin', at most 130 | -- 'cryptoGenericHashKeyBytesMax', and should typically be 'cryptoGenericHashKeyBytes'. 131 | -- It is the caller's responsibility to ensure that these hold. 132 | -- 133 | -- /See:/ [crypto_generichash_init()](https://doc.libsodium.org/hashing/generic_hashing#usage) 134 | -- 135 | -- @since 0.0.1.0 136 | foreign import capi "sodium.h crypto_generichash_init" 137 | cryptoGenericHashInit 138 | :: Ptr CryptoGenericHashState 139 | -- ^ Pointer to the hash state 140 | -> Ptr CUChar 141 | -- ^ Pointer to a key 142 | -> CSize 143 | -- ^ Length of the key 144 | -> CSize 145 | -- ^ Length of the result 146 | -> IO CInt 147 | -- ^ Returns 0 on success, -1 on error. 148 | 149 | -- | If you process a message in chunks, you can sequentially process each chunk by calling 'cryptoGenericHashUpdate' 150 | -- by providing a pointer to the previously initialised state, a pointer to the input chunk, 151 | -- and the length of the chunk in bytes. 152 | -- 153 | -- /See:/ [crypto_generichash_update()](https://doc.libsodium.org/hashing/generic_hashing#usage) 154 | -- 155 | -- @since 0.0.1.0 156 | foreign import capi "sodium.h crypto_generichash_update" 157 | cryptoGenericHashUpdate 158 | :: Ptr CryptoGenericHashState 159 | -- ^ Pointer to the hash state 160 | -> Ptr CUChar 161 | -- ^ Pointer to a chunk to be processed 162 | -> CULLong 163 | -- ^ Length of the chunk in bytes 164 | -> IO CInt 165 | -- ^ Returns 0 on success, -1 on error. 166 | 167 | -- | After processing everything you need with 'cryptoGenericHashUpdate', you can finalise the operation 168 | -- with 'cryptoGenericHashFinal'. 169 | -- 170 | -- /See:/ [crypto_generichash_final()](https://doc.libsodium.org/hashing/generic_hashing#usage) 171 | -- 172 | -- @since 0.0.1.0 173 | foreign import capi "sodium.h crypto_generichash_final" 174 | cryptoGenericHashFinal 175 | :: Ptr CryptoGenericHashState 176 | -- ^ The hash state used throughout the previous hashing operations. 177 | -> Ptr CUChar 178 | -- ^ The pointer to the resulting fingerprint. 179 | -> CSize 180 | -- ^ Size of the hash. 181 | -> IO CInt 182 | -- ^ Returns 0 on success, -1 if called twice. 183 | 184 | -- | This function creates a key of the recommended length 'cryptoGenericHashKeyBytes'. 185 | -- 186 | -- /See:/ [crypto_generichash_keygen()](https://doc.libsodium.org/hashing/generic_hashing#usage) 187 | -- 188 | -- @since 0.0.1.0 189 | foreign import capi "sodium.h crypto_generichash_keygen" 190 | cryptoGenericHashKeyGen 191 | :: Ptr CUChar 192 | -- ^ A pointer to the key 193 | -> IO () 194 | 195 | -- | Size of the generated hash. 196 | -- 197 | -- /See:/ [crypto_generichash_BYTES](https://doc.libsodium.org/hashing/generic_hashing#constants) 198 | -- 199 | -- @since 0.0.1.0 200 | foreign import capi "sodium.h value crypto_generichash_BYTES" 201 | cryptoGenericHashBytes :: CSize 202 | 203 | -- | Minimum size of a generated hash 204 | -- 205 | -- /See:/ [crypto_generichash_BYTES_MIN](https://doc.libsodium.org/hashing/generic_hashing#constants) 206 | -- 207 | -- @since 0.0.1.0 208 | foreign import capi "sodium.h value crypto_generichash_BYTES_MIN" 209 | cryptoGenericHashBytesMin :: CSize 210 | 211 | -- | Maximum size of a generated hash 212 | -- 213 | -- /See:/ [crypto_generichash_BYTES_MAX](https://doc.libsodium.org/hashing/generic_hashing#constants) 214 | -- 215 | -- @since 0.0.1.0 216 | foreign import capi "sodium.h value crypto_generichash_BYTES_MAX" 217 | cryptoGenericHashBytesMax :: CSize 218 | 219 | -- | Size of a generated key 220 | -- 221 | -- /See:/ [crypto_generichash_KEYBYTES](https://doc.libsodium.org/hashing/generic_hashing#constants) 222 | -- 223 | -- @since 0.0.1.0 224 | foreign import capi "sodium.h value crypto_generichash_KEYBYTES" 225 | cryptoGenericHashKeyBytes :: CSize 226 | 227 | -- | Minimum size of a generated key 228 | -- 229 | -- /See:/ [crypto_generichash_KEYBYTES_MIN](https://doc.libsodium.org/hashing/generic_hashing#constants) 230 | -- 231 | -- @since 0.0.1.0 232 | foreign import capi "sodium.h value crypto_generichash_KEYBYTES_MIN" 233 | cryptoGenericHashKeyBytesMin :: CSize 234 | 235 | -- | Maximum size of a generated key 236 | -- 237 | -- /See:/ [crypto_generichash_KEYBYTES_MAX](https://doc.libsodium.org/hashing/generic_hashing#constants) 238 | -- 239 | -- @since 0.0.1.0 240 | foreign import capi "sodium.h value crypto_generichash_KEYBYTES_MAX" 241 | cryptoGenericHashKeyBytesMax :: CSize 242 | 243 | -- | Size of a 'CryptoGenericHashState' 244 | -- 245 | -- @since 0.0.1.0 246 | foreign import capi "sodium.h crypto_generichash_blake2b_statebytes" 247 | cryptoGenericHashStateBytes :: CSize 248 | -------------------------------------------------------------------------------- /libsodium-bindings/src/LibSodium/Bindings/KeyDerivation.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CApiFFI #-} 2 | 3 | -- | 4 | -- Module: LibSodium.Bindings.KeyDerivation 5 | -- Description: Direct bindings to the key exchange functions implemented in Libsodium. The algorithm used is blake2b. 6 | -- Copyright: (C) Hécate Moonlight 2022 7 | -- License: BSD-3-Clause 8 | -- Maintainer: The Haskell Cryptography Group 9 | -- Stability: Stable 10 | -- Portability: GHC only 11 | module LibSodium.Bindings.KeyDerivation 12 | ( -- * Introduction 13 | -- $introduction 14 | 15 | -- ** Key Generation 16 | cryptoKDFKeygen 17 | , cryptoKDFDeriveFromKey 18 | 19 | -- ** Constants 20 | , cryptoKDFBytesMin 21 | , cryptoKDFBytesMax 22 | , cryptoKDFKeyBytes 23 | , cryptoKDFContextBytes 24 | ) 25 | where 26 | 27 | import Foreign (Ptr, Word64, Word8) 28 | import Foreign.C (CChar, CInt (CInt), CSize (CSize), CUChar) 29 | 30 | -- $introduction 31 | -- 32 | -- From a single, high-entropy key, you can derive multiple secret sub-keys. 33 | -- 34 | -- This API can derive up to 2⁶⁴ keys from a single key and context, and these 35 | -- sub-keys can have an arbitrary length between 128 (16 bytes) and 512 bits (64 bytes). 36 | 37 | -- | Generate a high-entropy key from which the sub-keys will be derived. 38 | -- 39 | -- /See:/ [crypto_kdf_keygen()](https://doc.libsodium.org/key_derivation) 40 | -- 41 | -- @since 0.0.1.0 42 | foreign import capi "sodium.h crypto_kdf_keygen" 43 | cryptoKDFKeygen 44 | :: Ptr Word8 45 | -- ^ Pointer that will hold the master key of length 'cryptoKDFKeyBytes' 46 | -> IO () 47 | 48 | -- | Derive a sub-key from a high-entropy secre key with a unique identifier. 49 | -- The identifier can be any value up to 2⁶⁴-1 50 | -- 51 | -- /See:/ [crypto_kdf_derive_from_key()](https://doc.libsodium.org/key_derivation) 52 | -- 53 | -- @since 0.0.1.0 54 | foreign import capi "sodium.h crypto_kdf_derive_from_key" 55 | cryptoKDFDeriveFromKey 56 | :: Ptr CUChar 57 | -- ^ Pointer that will hold the sub-key. 58 | -> CSize 59 | -- ^ Length of the sub-key. 60 | -> Word64 61 | -- ^ Identifier of the sub-key. Must not be reused for another sub-key. 62 | -> Ptr CChar 63 | -- ^ Pointer to the context, of size 'cryptoKDFContextBytes'. 64 | -> Ptr CUChar 65 | -- ^ Pointer to the master key, which will be of length 'cryptoKDFKeyBytes'. 66 | -> IO CInt 67 | -- ^ Returns 0 on success and -1 on error. 68 | 69 | -- == Constants 70 | 71 | -- | Minimum length of a sub-key. 72 | -- 73 | -- /See:/ [crypto_kdf_BYTES_MIN](https://doc.libsodium.org/key_derivation) 74 | -- 75 | -- @since 0.0.1.0 76 | foreign import capi "sodium.h value crypto_kdf_BYTES_MIN" 77 | cryptoKDFBytesMin :: CSize 78 | 79 | -- | Maximum length of a sub-key. 80 | -- 81 | -- /See:/ [crypto_kdf_BYTES_MAX](https://doc.libsodium.org/key_derivation) 82 | -- 83 | -- @since 0.0.1.0 84 | foreign import capi "sodium.h value crypto_kdf_BYTES_MAX" 85 | cryptoKDFBytesMax :: CSize 86 | 87 | -- | Length of a Context. 88 | -- 89 | -- /See:/ [crypto_kdf_CONTEXTBYTES](https://doc.libsodium.org/key_derivation) 90 | -- 91 | -- @since 0.0.1.0 92 | foreign import capi "sodium.h value crypto_kdf_CONTEXTBYTES" 93 | cryptoKDFContextBytes :: CSize 94 | 95 | -- | Length of the master key. 96 | -- 97 | -- /See:/ [crypto_kdf_KEYBYTES](https://doc.libsodium.org/key_derivation) 98 | -- 99 | -- @since 0.0.1.0 100 | foreign import capi "sodium.h value crypto_kdf_KEYBYTES" 101 | cryptoKDFKeyBytes :: CSize 102 | -------------------------------------------------------------------------------- /libsodium-bindings/src/LibSodium/Bindings/KeyExchange.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CApiFFI #-} 2 | {-# LANGUAGE ScopedTypeVariables #-} 3 | 4 | -- | 5 | -- Module: LibSodium.Bindings.KeyExchange 6 | -- Description: Direct bindings to the key exchange functions implemented in Libsodium 7 | -- Copyright: (C) Hécate Moonlight 2022 8 | -- License: BSD-3-Clause 9 | -- Maintainer: The Haskell Cryptography Group 10 | -- Stability: Stable 11 | -- Portability: GHC only 12 | module LibSodium.Bindings.KeyExchange 13 | ( -- * Introduction 14 | -- $introduction 15 | 16 | -- * Key Exchange 17 | 18 | -- ** Key generation 19 | cryptoKXKeyPair 20 | , cryptoKXSeedKeypair 21 | 22 | -- ** Client 23 | , cryptoKXClientSessionKeys 24 | 25 | -- ** Server 26 | , cryptoKXServerSessionKeys 27 | 28 | -- ** Constants 29 | , cryptoKXPublicKeyBytes 30 | , cryptoKXSecretKeyBytes 31 | , cryptoKXSeedBytes 32 | , cryptoKXSessionKeyBytes 33 | , cryptoKXPrimitive 34 | ) 35 | where 36 | 37 | import Foreign (Ptr) 38 | import Foreign.C (CChar, CInt (CInt), CSize (CSize), CUChar) 39 | 40 | -- $introduction 41 | -- 42 | -- The key exchange API allows two parties to securely compute a set of shared keys using their peer's public key, and 43 | -- their own secret key. 44 | 45 | -- | Create a new key pair. 46 | -- 47 | -- This function takes pointers to two empty buffers that will hold (respectively) the public and secret keys. 48 | -- 49 | -- /See:/ [crypto_kx_keypair()](https://doc.libsodium.org/key_exchange#usage) 50 | -- 51 | -- @since 0.0.1.0 52 | foreign import capi "sodium.h crypto_kx_keypair" 53 | cryptoKXKeyPair 54 | :: Ptr CUChar 55 | -- ^ The buffer that will hold the public key, of size 'cryptoKXPublicKeyBytes'. 56 | -> Ptr CUChar 57 | -- ^ The buffer that will hold the secret key, of size 'cryptoKXSecretKeyBytes'. 58 | -> IO CInt 59 | -- ^ Returns 0 on success, -1 on error. 60 | 61 | -- | Create a new key pair from a seed. 62 | -- 63 | -- This function takes pointers to two empty buffers that will hold (respectively) the public and secret keys, 64 | -- as well as the seed from which these keys will be derived. 65 | -- 66 | -- /See:/ [crypto_kx_seed_keypair()](https://doc.libsodium.org/key_exchange#usage) 67 | -- 68 | -- @since 0.0.1.0 69 | foreign import capi "sodium.h crypto_kx_seed_keypair" 70 | cryptoKXSeedKeypair 71 | :: Ptr CUChar 72 | -- ^ The buffer that will hold the public key, of size 'cryptoKXPublicKeyBytes'. 73 | -> Ptr CUChar 74 | -- ^ The buffer that will hold the secret key, of size 'cryptoKXSecretKeyBytes'. 75 | -> Ptr CUChar 76 | -- ^ The pointer to the seed from which the keys are derived. It is of size 'cryptoKXSeedBytes' bytes. 77 | -> IO CInt 78 | -- ^ Returns 0 on success, -1 on error. 79 | 80 | -- | Compute a pair of shared session keys (secret and public). 81 | -- 82 | -- These session keys are computed using: 83 | -- 84 | -- * The client's public key 85 | -- * The client's secret key 86 | -- * The server's public key 87 | -- 88 | -- The shared secret key should be used by the client to receive data from the server, whereas the shared 89 | -- public key should be used for data flowing to the server. 90 | -- 91 | -- If only one session key is required, either the pointer to the shared secret key or the pointer 92 | -- to the shared public key can be set to 'Foreign.nullPtr'. 93 | -- 94 | -- /See:/ [crypto_kx_client_session_keys()](https://doc.libsodium.org/key_exchange#usage) 95 | -- 96 | -- @since 0.0.1.0 97 | foreign import capi "sodium.h crypto_kx_client_session_keys" 98 | cryptoKXClientSessionKeys 99 | :: Ptr CUChar 100 | -- ^ A pointer to the buffer that will hold the shared secret key, of size 'cryptoKXSessionKeyBytes' bytes. 101 | -> Ptr CUChar 102 | -- ^ A pointer to the buffer that will hold the shared public key, of size 'cryptoKXSessionKeyBytes' bytes. 103 | -> Ptr CUChar 104 | -- ^ A pointer to the client's public key, of size 'cryptoKXPublicKeyBytes' bytes. 105 | -> Ptr CUChar 106 | -- ^ A pointer to the client's secret key, of size 'cryptoKXSecretKeyBytes' bytes. 107 | -> Ptr CUChar 108 | -- ^ A pointer to the server's public key, of size 'cryptoKXPublicKeyBytes' bytes. 109 | -> IO CInt 110 | -- ^ Returns 0 on success, -1 on error, such as when the server's public key is not acceptable. 111 | 112 | -- 113 | 114 | -- | Compute a pair of shared session keys (secret and public). 115 | -- 116 | -- These session keys are computed using: 117 | -- 118 | -- * The server's public key 119 | -- * The server's secret key 120 | -- * The client's public key 121 | -- 122 | -- The shared secret key should be used by the server to receive data from the client, whereas the shared 123 | -- public key should be used for data flowing to the client. 124 | -- 125 | -- If only one session key is required, either the pointer to the shared secret key or the pointer 126 | -- to the shared public key can be set to 'Foreign.nullPtr'. 127 | -- 128 | -- /See:/ [crypto_kx_server_session_keys()](https://doc.libsodium.org/key_exchange#usage) 129 | -- 130 | -- @since 0.0.1.0 131 | foreign import capi "sodium.h crypto_kx_server_session_keys" 132 | cryptoKXServerSessionKeys 133 | :: Ptr CUChar 134 | -- ^ A pointer to the buffer that will hold the shared secret key, of size 'cryptoKXSessionKeyBytes' bytes. 135 | -> Ptr CUChar 136 | -- ^ A pointer to the buffer that will hold the shared public key, of size 'cryptoKXSessionKeyBytes' bytes. 137 | -> Ptr CUChar 138 | -- ^ A pointer to the server's public key, of size 'cryptoKXPublicKeyBytes' bytes. 139 | -> Ptr CUChar 140 | -- ^ A pointer to the server's secret key, of size 'cryptoKXSecretKeyBytes' bytes. 141 | -> Ptr CUChar 142 | -- ^ A pointer to the client's public key, of size 'cryptoKXPublicKeyBytes' bytes. 143 | -> IO CInt 144 | -- ^ Returns 0 on success, -1 on error, such as when the server's public key is not acceptable. 145 | 146 | -- | Size of the public key in bytes. 147 | -- 148 | -- /See:/ [crypto_kx_PUBLICKEYBYTES](https://doc.libsodium.org/key_exchange#constants) 149 | -- 150 | -- @since 0.0.1.0 151 | foreign import capi "sodium.h value crypto_kx_PUBLICKEYBYTES" 152 | cryptoKXPublicKeyBytes :: CSize 153 | 154 | -- | Size of the secret key in bytes. 155 | -- 156 | -- /See:/ [crypto_kx_SECRETKEYBYTES](https://doc.libsodium.org/key_exchange#constants) 157 | -- 158 | -- @since 0.0.1.0 159 | foreign import capi "sodium.h value crypto_kx_SECRETKEYBYTES" 160 | cryptoKXSecretKeyBytes :: CSize 161 | 162 | -- | Size of the seed in bytes. 163 | -- 164 | -- /See:/ [crypto_kx_SEEDBYTES](https://doc.libsodium.org/key_exchange#constants) 165 | -- 166 | -- @since 0.0.1.0 167 | foreign import capi "sodium.h value crypto_kx_SEEDBYTES" 168 | cryptoKXSeedBytes :: CSize 169 | 170 | -- | Size of the session key in bytes. 171 | -- 172 | -- /See:/ [crypto_kx_SESSIONKEYBYTES](https://doc.libsodium.org/key_exchange#constants) 173 | -- 174 | -- @since 0.0.1.0 175 | foreign import capi "sodium.h value crypto_kx_SESSIONKEYBYTES" 176 | cryptoKXSessionKeyBytes :: CSize 177 | 178 | -- | Primitive used by this module 179 | -- 180 | -- /See:/ [crypto_kx_PRIMITIVE](https://doc.libsodium.org/key_exchange#constants) 181 | -- 182 | -- @since 0.0.1.0 183 | foreign import capi "sodium.h value crypto_kx_PRIMITIVE" 184 | cryptoKXPrimitive :: Ptr CChar 185 | -------------------------------------------------------------------------------- /libsodium-bindings/src/LibSodium/Bindings/Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE BangPatterns #-} 2 | {-# LANGUAGE CApiFFI #-} 3 | {-# LANGUAGE KindSignatures #-} 4 | {-# LANGUAGE ScopedTypeVariables #-} 5 | 6 | -- | Module: LibSodium.Bindings.Main 7 | -- Description: Wrappers for initialisation 8 | -- Copyright: (C) Koz Ross 2022 9 | -- License: BSD-3-Clause 10 | -- Maintainer: koz.ross@retro-freedom.nz 11 | -- Stability: Stable 12 | -- Portability: GHC only 13 | -- 14 | -- @libsodium@ requires initialisation before use. We provide a binding to the 15 | -- initialisation function, as well as some high-level wrappers for applications 16 | -- to use without needing to call an FFI wrapper. 17 | -- 18 | -- = Note 19 | -- 20 | -- If you are using @cryptography-libsodium@ as a dependency for a library, you 21 | -- are probably not interested in this; it's designed for application authors who 22 | -- need capabilities provided by @cryptography-libsodium@. 23 | module LibSodium.Bindings.Main 24 | ( -- * High-level wrappers 25 | secureMain 26 | , secureMainWithError 27 | 28 | -- * Low-level binding 29 | , sodiumInit 30 | ) 31 | where 32 | 33 | import Data.Kind (Type) 34 | import Foreign.C.Types (CInt (CInt)) 35 | import System.Exit (die) 36 | 37 | -- | Initialise all security-related functionality, then perform the given 38 | -- action. Abort with an error message if security-related functionality cannot 39 | -- be initialised. This will also indicate failure to the shell, as with 'die'. 40 | -- 41 | -- === Usage: 42 | -- 43 | -- > main :: IO () 44 | -- > main = secureMain doTheThingIActuallyWant 45 | -- 46 | -- @since 0.0.1.0 47 | secureMain 48 | :: forall (a :: Type) 49 | . IO a 50 | -- ^ Action that will perform cryptographic operations 51 | -> IO a 52 | secureMain = secureMainWithError (die "libsodium-bindings: Could not initialise secure functionality, aborting.") 53 | 54 | -- | Similar to 'secureMain', but allows responding to a failure of 55 | -- initialisation. 56 | -- 57 | -- === Usage: 58 | -- 59 | -- > main :: IO () 60 | -- > main = secureMainWith reportErrorWithLogging doTheThingIActuallyWant 61 | -- 62 | -- @since 0.0.1.0 63 | secureMainWithError 64 | :: forall (a :: Type) 65 | . IO a 66 | -- ^ Code to execute if there is an initialisation failure. 67 | -> IO a 68 | -- ^ Action that will perform cryptographic operations after libsodium is initialised. 69 | -> IO a 70 | secureMainWithError badPath goodPath = do 71 | !res <- sodiumInit 72 | if res == (-1) then badPath else goodPath 73 | 74 | -- | Initialise @libsodium@ for future use. This only needs to be called once, 75 | -- before any use of any other functionality, but multiple calls to this 76 | -- function are not harmful (just redundant). 77 | -- 78 | -- /See:/ [sodium_init()](https://doc.libsodium.org/usage) 79 | -- 80 | -- @since 0.0.1.0 81 | foreign import capi "sodium.h sodium_init" 82 | sodiumInit 83 | :: IO CInt 84 | -- ^ 0 if successful, -1 on failure, 1 on repeat calls 85 | -------------------------------------------------------------------------------- /libsodium-bindings/src/LibSodium/Bindings/Random.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CApiFFI #-} 2 | 3 | -- | Module: LibSodium.Bindings.Random 4 | -- Description: Secure random number generation 5 | -- Copyright: (C) Koz Ross 2022 6 | -- License: BSD-3-Clause 7 | -- Maintainer: koz.ross@retro-freedom.nz 8 | -- Stability: Stable 9 | -- Portability: GHC only 10 | -- 11 | -- A collection of functions for securely generating unpredictable data. This 12 | -- uses the best option on each platform, as follows: 13 | -- 14 | -- * On Windows, @RtlGenRandom@. 15 | -- * On FreeBSD and Linux, @getrandom@ syscall. 16 | -- * On other UNIX platforms, @\/dev\/urandom@. 17 | module LibSodium.Bindings.Random 18 | ( randombytesRandom 19 | , randombytesUniform 20 | , randombytesBuf 21 | ) 22 | where 23 | 24 | import Data.Word (Word32, Word8) 25 | import Foreign.C.Types (CSize (CSize)) 26 | import Foreign.Ptr (Ptr) 27 | 28 | -- | Produces an unpredictable four-byte value. 29 | -- 30 | -- /See:/ [randombytes_random()](https://doc.libsodium.org/generating_random_data#usage) 31 | -- 32 | -- @since 0.0.1.0 33 | foreign import capi "sodium.h randombytes_random" 34 | randombytesRandom :: IO Word32 35 | 36 | -- | Produces an unpredictable four-byte value not larger than the argument. This 37 | -- function guarantees a uniform distribution on results, even if the upper 38 | -- limit is not a power of 2. 39 | -- 40 | -- /See:/ [randombytes_uniform()](https://doc.libsodium.org/generating_random_data#usage) 41 | -- 42 | -- @since 0.0.1.0 43 | foreign import capi "sodium.h randombytes_uniform" 44 | randombytesUniform 45 | :: Word32 46 | -- ^ upper limit (exclusive) 47 | -> IO Word32 48 | 49 | -- | Fills a buffer of the given size with unpredictable bytes. 50 | -- 51 | -- /See:/ [randombytes_buf()](https://doc.libsodium.org/generating_random_data#usage) 52 | -- 53 | -- @since 0.0.1.0 54 | foreign import capi "sodium.h randombytes_buf" 55 | randombytesBuf 56 | :: Ptr Word8 57 | -- ^ Out-parameter to fill 58 | -> CSize 59 | -- ^ How many bytes to generate 60 | -> IO () 61 | -- ^ No meaningful return value 62 | -------------------------------------------------------------------------------- /libsodium-bindings/src/LibSodium/Bindings/SealedBoxes.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CApiFFI #-} 2 | 3 | -- | 4 | -- Module: LibSodium.Bindings.SealedBoxes 5 | -- Description: Direct bindings to the sealed boxes API of Libsodium 6 | -- License: BSD-3-Clause 7 | -- Maintainer: The Haskell Cryptography Group 8 | -- Stability: Stable 9 | -- Portability: GHC only 10 | module LibSodium.Bindings.SealedBoxes 11 | ( -- * Introduction 12 | -- $introduction 13 | 14 | -- * Functions 15 | cryptoBoxSeal 16 | , cryptoBoxSealOpen 17 | , cryptoBoxKeyPair 18 | , cryptoBoxSeedKeyPair 19 | 20 | -- * Constants 21 | , cryptoBoxSealbytes 22 | ) where 23 | 24 | import Foreign (Ptr) 25 | import Foreign.C (CInt (CInt), CSize (CSize), CUChar, CULLong (CULLong)) 26 | 27 | import LibSodium.Bindings.CryptoBox (cryptoBoxKeyPair, cryptoBoxSeedKeyPair) 28 | 29 | -- $introduction 30 | -- Sealed boxes are designed to anonymously send messages to a recipient 31 | -- given their public key. 32 | -- 33 | -- Only the recipient can decrypt these messages using their secret key. 34 | -- While the recipient can verify the integrity of the message, they cannot 35 | -- verify the identity of the sender. 36 | -- 37 | -- A message is encrypted using an ephemeral key pair, with the secret key being 38 | -- erased right after the encryption process. 39 | -- 40 | -- Without knowing the secret key used for a given message, the sender cannot decrypt 41 | -- the message later. Furthermore, without additional data, a message cannot be 42 | -- correlated with the identity of its sender. 43 | 44 | -- | @cryptoBoxSeal@ creates a new key pair for each message and attaches the public 45 | -- key to the ciphertext. The secret key is overwritten and is not accessible 46 | -- after this function returns. 47 | -- 48 | -- /See:/ [crypto_box_seal()](https://doc.libsodium.org/public-key_cryptography/sealed_boxes#usage) 49 | -- 50 | -- @since 0.0.1.0 51 | foreign import capi "sodium.h crypto_box_seal" 52 | cryptoBoxSeal 53 | :: Ptr CUChar 54 | -- ^ Buffer that will hold the encrypted message of size 55 | -- (size of original message + 'cryptoBoxSealbytes') bytes 56 | -> Ptr CUChar 57 | -- ^ Buffer that holds the plaintext message 58 | -> CULLong 59 | -- ^ Length of the plaintext message 60 | -> Ptr CUChar 61 | -- ^ Buffer that holds public key of size 62 | -- 'LibSodium.Bindings.CryptoBox.cryptoBoxPublicKeyBytes' bytes. 63 | -> IO CInt 64 | -- ^ Returns 0 on success and -1 on error. 65 | 66 | -- | 'cryptoBoxSealOpen' doesn't require passing the public key of 67 | -- the sender as the ciphertext already includes this information. 68 | -- 69 | -- Key pairs are compatible with operations from 'LibSodium.Bindings.CryptoBox' 70 | -- module and can be created using 'LibSodium.Bindings.CryptoBox.cryptoBoxKeyPair' 71 | -- or 'LibSodium.Bindings.CryptoBox.cryptoBoxSeedKeyPair'. 72 | -- 73 | -- /See:/ [crypto_box_seal_open()](https://doc.libsodium.org/public-key_cryptography/sealed_boxes#usage) 74 | -- 75 | -- @since 0.0.1.0 76 | foreign import capi "sodium.h crypto_box_seal_open" 77 | cryptoBoxSealOpen 78 | :: Ptr CUChar 79 | -- ^ Buffer that will hold the plaintext message of size 80 | -- (size of original message - 'cryptoBoxSealbytes') bytes 81 | -> Ptr CUChar 82 | -- ^ Buffer that holds the encrypted message. 83 | -> CULLong 84 | -- ^ Length of the encrypted message 85 | -> Ptr CUChar 86 | -- ^ Buffer that holds public key of size 87 | -- 'LibSodium.Bindings.CryptoBox.cryptoBoxPublicKeyBytes' bytes. 88 | -> Ptr CUChar 89 | -- ^ Buffer that holds secret key of size 90 | -- 'LibSodium.Bindings.CryptoBox.cryptoBoxSecretKeyBytes' bytes. 91 | -> IO CInt 92 | -- ^ Returns 0 on success and -1 on error. 93 | 94 | -- | Size diff in bytes between encrypted and plaintext messages, i.e. 95 | -- @cryptoBoxSealbytes = length encryptedMsg - length plaintextMsg@ 96 | -- 97 | -- @since 0.0.1.0 98 | foreign import capi "sodium.h value crypto_box_SEALBYTES" 99 | cryptoBoxSealbytes :: CSize 100 | -------------------------------------------------------------------------------- /libsodium-bindings/src/LibSodium/Bindings/Secretbox.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CApiFFI #-} 2 | {-# LANGUAGE Trustworthy #-} 3 | 4 | -- | 5 | -- Module: LibSodium.Bindings.Secretbox 6 | -- Description: Direct bindings to the secretbox API of Libsodium 7 | -- License: BSD-3-Clause 8 | -- Maintainer: The Haskell Cryptography Group 9 | -- Stability: Stable 10 | -- Portability: GHC only 11 | module LibSodium.Bindings.Secretbox 12 | ( -- * Introduction 13 | -- $introduction 14 | 15 | -- * Secretbox 16 | 17 | -- ** Keygen 18 | cryptoSecretboxKeygen 19 | 20 | -- ** Easy 21 | , cryptoSecretboxEasy 22 | , cryptoSecretboxOpenEasy 23 | 24 | -- ** Detached 25 | , cryptoSecretboxDetached 26 | , cryptoSecretboxOpenDetached 27 | 28 | -- ** Constants 29 | , cryptoSecretboxKeyBytes 30 | , cryptoSecretboxNonceBytes 31 | , cryptoSecretboxMACBytes 32 | , cryptoSecretboxPrimitive 33 | , cryptoSecretboxMessageBytesMax 34 | ) 35 | where 36 | 37 | import Foreign (Ptr) 38 | import Foreign.C (CChar, CInt (CInt), CSize (CSize), CUChar, CULLong (CULLong)) 39 | 40 | -- $introduction 41 | -- This API allows encrypting a message using a secret key and a nonce. 42 | -- The ciphertext is accompanied by an authentication tag. 43 | -- 44 | -- 45 | -- It comes in two flavours: 46 | -- 47 | -- [easy] Both the ciphertext and authentication tag are stored in the same buffer. 48 | -- [detached] The ciphertext and authentication tag may be stored in separate buffers. 49 | -- 50 | -- 51 | -- The same key is used for both encryption and decryption, so it must be kept secret. 52 | -- A key can be generated using the 'cryptoSecretboxKeygen' primitive. 53 | -- 54 | -- 55 | -- Each message must use a unique nonce, which may be generated with the 'LibSodium.Bindings.Random.randombytesBuf' primitive. 56 | -- The nonce does not need to be kept secret but should never be reused with the same secret key. 57 | -- 58 | -- For more information see the upstream docs: 59 | 60 | -- | Generate a key that can be used by the primitives of the secretbox API. 61 | -- 62 | -- /See:/ [crypto_secretbox_keygen()](https://doc.libsodium.org/secret-key_cryptography/secretbox#detached-mode) 63 | -- 64 | -- @since 0.0.1.0 65 | foreign import capi "sodium.h crypto_secretbox_keygen" 66 | cryptoSecretboxKeygen 67 | :: Ptr CUChar 68 | -- ^ key buffer of length 'cryptoSecretboxKeyBytes' 69 | -> IO () 70 | 71 | -- | Encrypt a message using a secret key and nonce. 72 | -- 73 | -- The message and ciphertext buffers may overlap enabling in-place encryption, but note that the 74 | -- ciphertext will be 'cryptoSecretboxMACBytes' bytes longer than the message. 75 | -- 76 | -- /See:/ [crytpo_secretbox_easy()](https://doc.libsodium.org/secret-key_cryptography/secretbox#combined-mode) 77 | -- 78 | -- @since 0.0.1.0 79 | foreign import capi "sodium.h crypto_secretbox_easy" 80 | cryptoSecretboxEasy 81 | :: Ptr CUChar 82 | -- ^ A pointer to the buffer that will hold the ciphertext. 83 | -- The length of the ciphertext is the length of the message in bytes plus 'cryptoSecretboxMACBytes' bytes. 84 | -> Ptr CUChar 85 | -- ^ A pointer to the buffer holding the message to be encrypted. 86 | -> CULLong 87 | -- ^ The length of the message in bytes. 88 | -> Ptr CUChar 89 | -- ^ A pointer to the nonce of size 'cryptoSecretboxNonceBytes' bytes. 90 | -> Ptr CUChar 91 | -- ^ A pointer to the secret key of size 'cryptoSecretboxKeyBytes' bytes. 92 | -> IO CInt 93 | -- ^ Returns 0 on success and -1 on error. 94 | 95 | -- | Verify and decrypt ciphertext using a secret key and nonce. 96 | -- 97 | -- The message and ciphertext buffers may overlap enabling in-place decryption, but note that the 98 | -- message will be 'cryptoSecretboxMACBytes' bytes shorter than the ciphertext. 99 | -- 100 | -- /See:/ [crypto_secretbox_open_easy()](https://doc.libsodium.org/secret-key_cryptography/secretbox#combined-mode) 101 | -- 102 | -- @since 0.0.1.0 103 | foreign import capi "sodium.h crypto_secretbox_open_easy" 104 | cryptoSecretboxOpenEasy 105 | :: Ptr CUChar 106 | -- ^ A pointer to the buffer that will hold the decrypted message. 107 | -- The length of the message is the length of the ciphertext in bytes minus 'cryptoSecretboxMACBytes' bytes. 108 | -> Ptr CUChar 109 | -- ^ A pointer to the buffer holding the ciphertext to be verified and decrypted. 110 | -> CULLong 111 | -- ^ The length of the ciphertext in bytes. 112 | -> Ptr CUChar 113 | -- ^ A pointer to the nonce of size 'cryptoSecretboxNonceBytes' bytes. 114 | -> Ptr CUChar 115 | -- ^ A pointer to the secret key of size 'cryptoSecretboxKeyBytes' bytes. 116 | -> IO CInt 117 | -- ^ Returns 0 on success and -1 on error. 118 | 119 | -- | Encrypt a message using a secret key and nonce. 120 | -- 121 | -- /See:/ [crypto_secretbox_detached()](https://doc.libsodium.org/secret-key_cryptography/secretbox#detached-mode) 122 | -- 123 | -- @since 0.0.1.0 124 | foreign import capi "sodium.h crypto_secretbox_detached" 125 | cryptoSecretboxDetached 126 | :: Ptr CUChar 127 | -- ^ A pointer to the buffer that will hold the ciphertext. This will have the same length as the message. 128 | -> Ptr CUChar 129 | -- ^ A pointer to the buffer that will hold the authentication tag. 130 | -- This will be of length 'cryptoSecretboxMACBytes' bytes. 131 | -> Ptr CUChar 132 | -- ^ A pointer to the buffer holding the message to be encrypted. 133 | -> CULLong 134 | -- ^ The length of the message in bytes. 135 | -> Ptr CUChar 136 | -- ^ A pointer to the nonce of size 'cryptoSecretboxNonceBytes' bytes. 137 | -> Ptr CUChar 138 | -- ^ A pointer to the secret key of size 'cryptoSecretboxKeyBytes' bytes. 139 | -> IO CInt 140 | -- ^ Returns 0 on success and -1 on error. 141 | 142 | -- | Verify and decrypt ciphertext using a secret key and nonce 143 | -- 144 | -- /See:/ [crypto_secretbox_open_detached()](https://doc.libsodium.org/secret-key_cryptography/secretbox#detached-mode) 145 | -- 146 | -- @since 0.0.1.0 147 | foreign import capi "sodium.h crypto_secretbox_open_detached" 148 | cryptoSecretboxOpenDetached 149 | :: Ptr CUChar 150 | -- ^ A pointer to the buffer that will hold the decrypted message. This will have the same length as the ciphertext. 151 | -> Ptr CUChar 152 | -- ^ A pointer to the buffer holding the ciphertext to be decrypted. 153 | -> Ptr CUChar 154 | -- ^ A pointer to the buffer holding the authentication tag to be verified. 155 | -> CULLong 156 | -- ^ The length of the ciphertext in bytes. 157 | -> Ptr CUChar 158 | -- ^ A pointer to the nonce of size 'cryptoSecretboxNonceBytes' bytes. 159 | -> Ptr CUChar 160 | -- ^ A pointer to the secret key of size 'cryptoSecretboxKeyBytes' bytes. 161 | -> IO CInt 162 | -- ^ Returns 0 on success and -1 on error. 163 | 164 | -- | The length of a secretbox key in bytes. 165 | -- 166 | -- /See:/ [crypto_secretbox_KEYBYTES](https://doc.libsodium.org/secret-key_cryptography/secretbox#constants) 167 | -- 168 | -- @since 0.0.1.0 169 | foreign import capi "sodium.h value crypto_secretbox_KEYBYTES" 170 | cryptoSecretboxKeyBytes :: CSize 171 | 172 | -- | The length of a secretbox nonce in bytes. 173 | -- 174 | -- /See:/ [crypto_secretbox_NONCEBYTES](https://doc.libsodium.org/secret-key_cryptography/secretbox#constants) 175 | -- 176 | -- @since 0.0.1.0 177 | foreign import capi "sodium.h value crypto_secretbox_NONCEBYTES" 178 | cryptoSecretboxNonceBytes :: CSize 179 | 180 | -- | The length of a secretbox authentication tag in bytes. 181 | -- 182 | -- /See:/ [crypto_secretbox_MACBYTES](https://doc.libsodium.org/secret-key_cryptography/secretbox#constants) 183 | -- 184 | -- @since 0.0.1.0 185 | foreign import capi "sodium.h value crypto_secretbox_MACBYTES" 186 | cryptoSecretboxMACBytes :: CSize 187 | 188 | -- | The underlying cryptographic algorithm used to implement the secretbox API. 189 | -- 190 | -- /See:/ [crypto_secretbox_PRIMITIVE](https://doc.libsodium.org/secret-key_cryptography/secretbox#algorithm-details) 191 | -- 192 | -- @since 0.0.1.0 193 | foreign import capi "sodium.h value crypto_secretbox_PRIMITIVE" 194 | cryptoSecretboxPrimitive :: Ptr CChar 195 | 196 | -- | Maximum length of a message in bytes that can be encrypted using the secretbox API. 197 | -- 198 | -- /See:/ [crypto_secretbox_MESSAGEBYTES_MAX](https://doc.libsodium.org/secret-key_cryptography/secretbox#constants) 199 | -- 200 | -- @since 0.0.1.0 201 | foreign import capi "sodium.h value crypto_secretbox_MESSAGEBYTES_MAX" 202 | cryptoSecretboxMessageBytesMax :: CSize 203 | -------------------------------------------------------------------------------- /libsodium-bindings/src/LibSodium/Bindings/SecureMemory.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CApiFFI #-} 2 | {-# LANGUAGE ScopedTypeVariables #-} 3 | 4 | -- | 5 | -- 6 | -- Module: LibSodium.Bindings.SecureMemory 7 | -- Description: Direct bindings to the libsodium secure memory functions 8 | -- Copyright: (C) Hécate Moonlight 2022 9 | -- License: BSD-3-Clause 10 | -- Maintainer: The Haskell Cryptography Group 11 | -- Portability: GHC only 12 | module LibSodium.Bindings.SecureMemory 13 | ( -- ** Introduction 14 | -- $introduction 15 | 16 | -- ** Zeroing memory 17 | sodiumMemZero 18 | 19 | -- ** Locking memory 20 | , sodiumMlock 21 | , sodiumMunlock 22 | 23 | -- ** Allocating memory 24 | , sodiumMalloc 25 | , sodiumAllocArray 26 | , sodiumFree 27 | , finalizerSodiumFree 28 | ) 29 | where 30 | 31 | import Data.Word (Word8) 32 | import Foreign (FinalizerPtr, Ptr) 33 | import Foreign.C.Types (CInt (CInt), CSize (CSize)) 34 | 35 | -- $introduction 36 | -- This module provides bindings to the secure memory functions provided by Libsodium. 37 | -- It is intended to be qualified on import: 38 | -- 39 | -- > import qualified LibSodium.Bindings.SecureMemory as SecureMemory 40 | -- 41 | -- It is recommended to disable swap partitions on machines processing sensitive 42 | -- data or, as a second choice, use encrypted swap partitions. 43 | -- 44 | -- For similar reasons, on Unix systems, one should also disable core dumps when 45 | -- running crypto code outside a development environment. 46 | -- This can be achieved using a shell built-in such as @ulimit@ or programmatically 47 | -- using [@setResourceLimit@](https://hackage.haskell.org/package/unix/docs/System-Posix-Resource.html#v:setResourceLimit): 48 | -- 49 | -- >>> setResourceLimit ResourceCoreFileSize (ResourceLimits 0 0) 50 | -- 51 | -- On operating systems where this feature is implemented, kernel crash dumps 52 | -- should also be disabled. 53 | -- 54 | -- The 'sodiumMlock' function wraps @mlock(2)@ and 55 | -- [@VirtualLock()@](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtuallock). 56 | -- 57 | -- Note: Many systems place limits on the amount of memory that may be locked 58 | -- by a process. Care should be taken to raise those limits (e.g. Unix ulimits) 59 | -- where necessary. 60 | 61 | -- | Overwrite the memory region starting at the pointer 62 | -- address with zeros. 63 | -- 64 | -- @memset()@ and hand-written code can be silently stripped out by 65 | -- an optimizing compiler or the linker. 66 | -- 67 | -- This function tries to effectively zero the amount of bytes starting 68 | -- at the provided pointer, even if optimizations are being applied to the code. 69 | -- 70 | -- /See:/ [sodium_memzero()](https://doc.libsodium.org/memory_management) 71 | -- 72 | -- @since 0.0.1.0 73 | foreign import capi "sodium.h sodium_memzero" 74 | sodiumMemZero 75 | :: Ptr Word8 76 | -- ^ Start pointer 77 | -> CSize 78 | -- ^ Length in bytes of the area to zero 79 | -> IO () 80 | 81 | -- | Lock a memory region starting at the pointer 82 | -- address. This can help avoid swapping sensitive 83 | -- data to disk. 84 | -- 85 | -- /See:/ [sodium_mlock()](https://doc.libsodium.org/memory_management) 86 | -- 87 | -- @since 0.0.1.0 88 | foreign import capi "sodium.h sodium_mlock" 89 | sodiumMlock 90 | :: Ptr Word8 91 | -- ^ Start pointer 92 | -> CSize 93 | -- ^ Size of the memory region to lock 94 | -> IO CInt 95 | -- ^ Returns 0 on success, -1 if any system limit is reached. 96 | 97 | -- | Unlock the memory region by overwriting it with zeros and and flagging the 98 | -- pages as swappable again. Calling 'sodiumMemZero' prior to 'sodiumMunlock' is thus not required. 99 | -- 100 | -- On systems where it is supported, 'sodiumMlock' also wraps @madvise(2)@ and advises the kernel not to include the locked memory in core dumps. The 'sodiumMunlock' 101 | -- function also undoes this additional protection. 102 | -- 103 | -- /See:/ [sodium_munlock()](https://doc.libsodium.org/memory_management) 104 | -- 105 | -- @since 0.0.1.0 106 | foreign import capi "sodium.h sodium_munlock" 107 | sodiumMunlock 108 | :: Ptr Word8 109 | -- ^ Start pointer 110 | -> CSize 111 | -- ^ Size of the memory region to unlock 112 | -> IO CInt 113 | 114 | -- | This function takes an amount (called @size@) and returns a pointer from which 115 | -- exactly @size@ contiguous bytes of memory can be accessed. The pointer may be 116 | -- 'Foreign.Ptr.nullPtr' and there may be an error when allocating memory, 117 | -- through @errno@. Upon failure, @errno@ will be set to 'Foreign.C.Error.eNOMEM' 118 | -- 119 | -- It is recommended that the caller use "Foreign.C.Error" to handle potential failure. 120 | -- 121 | -- Moreover, 'LibSodium.Bindings.Main.sodiumInit' must be called before using this 122 | -- function. 123 | -- 124 | -- === Explanation 125 | -- The allocated region is placed at the end of a page boundary, 126 | -- immediately followed by a guard page (or an emulation, 127 | -- if unsupported by the platform). As a result, accessing memory past the end of the 128 | -- region will immediately terminate the application. 129 | -- 130 | -- A canary is also placed right before the returned pointer. Modifications of this 131 | -- canary are detected when trying to free the allocated region with 'sodiumFree' 132 | -- and cause the application to immediately terminate. 133 | -- 134 | -- If supported by the platform, an additional guard page is placed before this canary 135 | -- to make it less likely for sensitive data to be accessible when reading past the end 136 | -- of an unrelated region. 137 | -- The allocated region is filled with 0xdb bytes to help catch bugs due to 138 | -- uninitialized data. 139 | -- 140 | -- In addition, @mlock(2)@ is called on the region to help avoid it being swapped to disk. 141 | -- Note however that @mlock(2)@ may not be supported, may fail or may be a no-op, 142 | -- in which case 'sodiumMalloc' will return the memory regardless, but it will not be 143 | -- locked. If you specifically need to rely on memory locking, consider calling 144 | -- 'sodiumMlock' and checking its return value. 145 | -- 146 | -- On operating systems supporting @MAP_NOCORE@ or @MADV_DONTDUMP@, memory allocated this 147 | -- way will also not be part of core dumps. 148 | -- The returned address will not be aligned if the allocation size is not a multiple 149 | -- of the required alignment. 150 | -- For this reason, 'sodiumMalloc' should not be used with packed or variable-length 151 | -- structures unless the size given to 'sodiumMalloc' is rounded up to ensure proper 152 | -- alignment. 153 | -- 154 | -- All the structures used by libsodium can safely be allocated using 155 | -- 'sodiumMalloc'. 156 | -- 157 | -- Allocating 0 bytes is a valid operation. It returns a pointer that can be 158 | -- successfully passed to 'sodiumFree'. 159 | -- 160 | -- ⚠️ This is not a general-purpose allocation function, and requires 3 or 4 extra 161 | -- pages of virtual memory. Since it is very expensive, do not use it to allocate 162 | -- every-day memory. 163 | -- 164 | -- /See:/ [sodium_malloc()](https://doc.libsodium.org/memory_management) 165 | -- 166 | -- @since 0.0.1.0 167 | foreign import capi "sodium.h sodium_malloc" 168 | sodiumMalloc 169 | :: forall a 170 | . CSize 171 | -- ^ Amount of memory to allocate 172 | -> IO (Ptr a) 173 | 174 | -- | This function takes an amount of objects and the size of each object, and 175 | -- returns a pointer from which this amount of objects that are of the specified 176 | -- size each can be accessed. 177 | -- 178 | -- It provides the same guarantees as 'sodiumMalloc' but also protects against 179 | -- arithmetic overflows when @count * size@ exceeds @SIZE_MAX@. 180 | -- 181 | -- /See:/ [sodium_allocarray()](https://doc.libsodium.org/memory_management) 182 | -- 183 | -- @since 0.0.1.0 184 | foreign import capi "sodium.h sodium_allocarray" 185 | sodiumAllocArray 186 | :: forall a 187 | . CSize 188 | -- ^ Amount of objects 189 | -> CSize 190 | -- ^ Size of each objects 191 | -> IO (Ptr a) 192 | 193 | -- | Unlock and deallocate memory allocated using 'sodiumMalloc' or 'sodiumAllocArray'. 194 | -- 195 | -- The memory region is filled with zeros before the deallocation. 196 | -- 197 | -- /See:/ [sodium_free()](https://doc.libsodium.org/memory_management) 198 | -- 199 | -- @since 0.0.1.0 200 | foreign import capi "sodium.h sodium_free" 201 | sodiumFree 202 | :: forall a 203 | . Ptr a 204 | -> IO () 205 | 206 | -- | Function pointer to use as 'Foreign.ForeignPtr' finalizer for sodium-allocated memory. 207 | -- 208 | -- The memory region is filled with zeros before the deallocation. 209 | -- 210 | -- /See:/ [sodium_free()](https://doc.libsodium.org/memory_management) 211 | -- 212 | -- @since 0.0.1.0 213 | foreign import capi "sodium.h &sodium_free" 214 | finalizerSodiumFree 215 | :: forall a 216 | . FinalizerPtr a 217 | -------------------------------------------------------------------------------- /libsodium-bindings/src/LibSodium/Bindings/ShortHashing.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CApiFFI #-} 2 | 3 | -- | 4 | -- Module: LibSodium.Bindings.ShortHashing 5 | -- Description: Short but unpredictable hashes based on SipHash-2-4. 6 | -- Copyright: (C) Hécate Moonlight 7 | -- License: BSD-3-Clause 8 | -- Maintainer: The Haskell Cryptography Group 9 | -- Stability: Stable 10 | -- Portability: GHC only 11 | -- 12 | -- The short-input hashing functions have a variety of use-cases, such as: 13 | -- 14 | -- * Hash Tables 15 | -- * Probabilistic data structures, such as Bloom filters 16 | -- * Integrity checking in interactive protocols 17 | -- 18 | -- Note however that the output of 'cryptoShortHash' is only 64 bit. Therefore, it should not be considered resistant to collisions. 19 | -- 20 | -- The 'cryptoShortHashX24' function has a 128-bit output which is more resistant to collisions. 21 | module LibSodium.Bindings.ShortHashing 22 | ( -- ** Functions 23 | cryptoShortHashKeyGen 24 | , cryptoShortHash 25 | , cryptoShortHashX24KeyGen 26 | , cryptoShortHashX24 27 | 28 | -- ** Constants 29 | , cryptoShortHashKeyBytes 30 | , cryptoShortHashBytes 31 | , cryptoShortHashSipHashX24KeyBytes 32 | , cryptoShortHashSipHashX24Bytes 33 | ) where 34 | 35 | import Data.Word (Word8) 36 | import Foreign.C (CInt (CInt), CSize (CSize), CUChar, CULLong (CULLong)) 37 | import Foreign.Ptr (Ptr, castPtr) 38 | 39 | import LibSodium.Bindings.Random (randombytesBuf) 40 | 41 | -- | Create a secret key of size 'cryptoShortHashKeyBytes'. 42 | -- 43 | -- It is implemented by libsodium with 'randombytesBuf' 44 | -- 45 | -- /See:/ [crypto_shorthash_keygen()](https://github.com/jedisct1/libsodium/blob/1.0.18/src/libsodium/crypto_shorthash/crypto_shorthash.c#L30-L34) 46 | -- 47 | -- @since 0.0.1.0 48 | foreign import capi "sodium.h crypto_shorthash_keygen" 49 | cryptoShortHashKeyGen 50 | :: Ptr CUChar 51 | -- ^ Buffer that will hold the secret key. 52 | -> IO () 53 | 54 | -- | Hash an input message with a secret key. 55 | -- The secret key is of size 'cryptoShortHashKeyBytes' 56 | -- and can be generated with 'cryptoShortHashKeyGen'. 57 | -- 58 | -- /See:/ [crypto_shorthash()](https://doc.libsodium.org/hashing/short-input_hashing#usage) 59 | -- 60 | -- @since 0.0.1.0 61 | foreign import capi "sodium.h crypto_shorthash" 62 | cryptoShortHash 63 | :: Ptr CUChar 64 | -- ^ Buffer that will hold the fingerprint, of size 'cryptoShortHashBytes'. 65 | -> Ptr CUChar 66 | -- ^ Buffer that holds the input message. 67 | -> CULLong 68 | -- ^ Length of the input message. 69 | -> Ptr CUChar 70 | -- ^ Buffer that holds the secret key, of size 'cryptoShortHashKeyBytes'. 71 | -> IO CInt 72 | -- ^ The function returns -1 if the hashing fails and 0 on success. 73 | 74 | -- | Create a secret key of size 'cryptoShortHashSipHashX24KeyBytes'. 75 | -- 76 | -- It is implemented with 'randombytesBuf' 77 | -- 78 | -- @since 0.0.1.0 79 | cryptoShortHashX24KeyGen 80 | :: Ptr CUChar 81 | -- ^ Buffer that will hold the secret key. 82 | -> IO () 83 | cryptoShortHashX24KeyGen ptr = 84 | randombytesBuf (castPtr ptr :: Ptr Word8) cryptoShortHashSipHashX24KeyBytes 85 | 86 | -- | Hash an input message with a secret key to a 128-bit fingerprint. 87 | -- 88 | -- The secret key can be generated with 'cryptoShortHashX24KeyGen'. 89 | -- 90 | -- /See:/ [crypto_shorthash_siphashx24()](https://github.com/jedisct1/libsodium/blob/1.0.18/src/libsodium/crypto_shorthash/siphash24/ref/shorthash_siphashx24_ref.c#L6) 91 | -- 92 | -- @since 0.0.1.0 93 | foreign import capi "sodium.h crypto_shorthash_siphashx24" 94 | cryptoShortHashX24 95 | :: Ptr CUChar 96 | -- ^ Buffer that will hold the fingerprint, of size 'cryptoShortHashSipHashX24Bytes'. 97 | -> Ptr CUChar 98 | -- ^ Buffer that holds the input message. 99 | -> CULLong 100 | -- ^ Length of the input message. 101 | -> Ptr CUChar 102 | -- ^ Buffer that holds the secret key, of size 'cryptoShortHashSipHashX24KeyBytes'. 103 | -> IO CInt 104 | -- ^ The function returns -1 if the hashing fails and 0 on success. 105 | 106 | -- === Constants 107 | 108 | -- | Size of the key generated by 'cryptoShortHashKeyGen'. 109 | -- 110 | -- /See:/ [crypto_shorthash_KEYBYTES](https://doc.libsodium.org/hashing/short-input_hashing#constants) 111 | -- 112 | -- @since 0.0.1.0 113 | foreign import capi "sodium.h value crypto_shorthash_KEYBYTES" 114 | cryptoShortHashKeyBytes :: CSize 115 | 116 | -- | Size of the fingerprint computed by 'cryptoShortHash'. 117 | -- 118 | -- /See:/ [crypto_shorthash_BYTES](https://doc.libsodium.org/hashing/short-input_hashing#constants) 119 | -- 120 | -- @since 0.0.1.0 121 | foreign import capi "sodium.h value crypto_shorthash_BYTES" 122 | cryptoShortHashBytes :: CSize 123 | 124 | -- | Size of the key generated by 'cryptoShortHashKeyGen'. 125 | -- 126 | -- /See:/ [crypto_shorthash_siphashx24_BYTES](https://github.com/jedisct1/libsodium/blob/1.0.18/src/libsodium/include/sodium/crypto_shorthash_siphash24.h#LL32C9-L32C42) 127 | -- 128 | -- @since 0.0.1.0 129 | foreign import capi "sodium.h value crypto_shorthash_siphashx24_KEYBYTES" 130 | cryptoShortHashSipHashX24KeyBytes :: CSize 131 | 132 | -- | Size of the fingerprint computed by 'cryptoShortHashX24'. 133 | -- 134 | -- /See:/ [crypto_shorthash_siphashx24_KEYBYTES](https://github.com/jedisct1/libsodium/blob/1.0.18/src/libsodium/include/sodium/crypto_shorthash_siphash24.h#L36) 135 | -- 136 | -- @since 0.0.1.0 137 | foreign import capi "sodium.h value crypto_shorthash_siphashx24_BYTES" 138 | cryptoShortHashSipHashX24Bytes :: CSize 139 | -------------------------------------------------------------------------------- /libsodium-bindings/src/LibSodium/Bindings/Utils.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CApiFFI #-} 2 | {-# LANGUAGE ScopedTypeVariables #-} 3 | {-# LANGUAGE Trustworthy #-} 4 | {-# OPTIONS_GHC -Wno-unused-imports #-} 5 | 6 | -- | 7 | -- Module: LibSodium.Bindings.Utils 8 | -- Description: Helpers exposed by the libsodium C library 9 | -- Copyright: (C) Seth Livy 2022 10 | -- License: BSD-3-Clause 11 | -- Maintainer: The Haskell Cryptography Group 12 | -- Stability: Stable 13 | -- Portability: GHC only 14 | -- 15 | -- These are bindings to some of libsodium's [utils.h](https://github.com/jedisct1/libsodium/blob/master/src/libsodium/include/sodium/utils.h). 16 | -- Included are Hex and Base64 encoding/decoding functions along with a constant-time @memcmp@ for handling secret data. 17 | module LibSodium.Bindings.Utils 18 | ( -- * Low-level binding 19 | sodiumMemcmp 20 | , sodiumBin2Hex 21 | -- , sodiumHex2Bin 22 | , sodiumBin2Base64 23 | -- , sodiumBase642Bin 24 | 25 | -- * Constants 26 | , sodiumBase64VariantOriginal 27 | , sodiumBase64VariantOriginalNoPadding 28 | , sodiumBase64VariantURLSafe 29 | , sodiumBase64VariantURLSafeNoPadding 30 | ) 31 | where 32 | 33 | import Foreign (Ptr) 34 | import Foreign.C (CChar (..), CInt (..), CSize (..), CUChar (..)) 35 | import Foreign.C.String 36 | 37 | -- | Constant-time comparison function. 38 | -- 39 | -- This function is not a lexicographic comparator and should be never 40 | -- used for this purpose. It should only be used when comparing two pieces 41 | -- of secret data, such as keys or authentication tags. 42 | -- 43 | -- @since 0.0.1.0 44 | foreign import capi "sodium.h sodium_memcmp" 45 | sodiumMemcmp 46 | :: Ptr CUChar 47 | -- ^ First pointer to some secret data. 48 | -> Ptr CUChar 49 | -- ^ Second pointer to some secret data. 50 | -- Must be the same length as the first pointer. 51 | -> CSize 52 | -- ^ The length of bytes that pointed to by both previous arguments. 53 | -> IO CInt 54 | -- ^ 0 if successful, -1 on failure. 55 | 56 | -- | Encode bytes to a hexidecimal string. Constant-time. 57 | -- 58 | -- @since 0.0.1.0 59 | foreign import capi "sodium.h sodium_bin2hex" 60 | sodiumBin2Hex 61 | :: CString 62 | -- ^ @hex@, The output buffer. 63 | -> CSize 64 | -- ^ @hex_len@, The maximum number of bytes this function is allowed to write 65 | -- to the output buffer. Must be at least @bin_len * 2 + 1@ bytes long. 66 | -> Ptr CUChar 67 | -- ^ @bin@, The input buffer. 68 | -> CSize 69 | -- ^ @bin_len@, The length of the input buffer. 70 | -> IO CString 71 | -- ^ The return string, terminated with a null byte. 72 | 73 | {- 74 | 75 | Due to a deficiency in Haskell's C FFI regarding nested pointers, 76 | this function and its Base64 counterpart have been commented out. 77 | 78 | The C shim that GHC generates ignores the @const@ qualifier in the 79 | type for @hex_end@, leading to multiple type errors. 80 | 81 | There is a pull request in GHC to fix this that is to ship with GHC 9.6. 82 | https://gitlab.haskell.org/ghc/ghc/-/commit/4f70a8a0b5db49ff249271faefec14bf1421f365 83 | 84 | -} 85 | 86 | {- 87 | -- | Decode a hexadecimal string to bytes. Constant-time. 88 | foreign import capi "sodium.h sodium_hex2bin" 89 | sodiumHex2Bin 90 | :: Ptr CUChar 91 | -- ^ @bin@, The output buffer. 92 | -> CSize 93 | -- ^ @bin_maxlen@, The maximum length of the output buffer. 94 | -> CString 95 | -- ^ @hex@, The input string. 96 | -> CSize 97 | -- ^ @hex_len@, The length of the input. 98 | -> CString 99 | -- ^ @ignore@, a string of characters for the parser to skip. 100 | -- For example, the string ": " allows colons and spaces in the input. 101 | -- These characters will ignored and will not be present in the output. 102 | -> Ptr CSize 103 | -- ^ @bin_len@, The length of the output buffer. 104 | -> Ptr CString 105 | -- ^ @hex_end@, A pointer to the end of the input string. 106 | -- If this isn't null, then it will be set to the first byte after the last 107 | -- valid parsed character. 108 | -> IO CInt 109 | -- ^ 0 if successful, -1 on failure. Common failures are if the string 110 | -- couldn't be fully parsed or if the parsed string is longer than the 111 | -- maximum amount of bytes allocated to store it. 112 | -} 113 | 114 | -- | Encode bytes to a Base64 string. Constant-time. 115 | foreign import capi "sodium.h sodium_bin2base64" 116 | sodiumBin2Base64 117 | :: CString 118 | -- ^ @b64@, The output buffer. 119 | -> CSize 120 | -- ^ @b64_maxlen@, The maximum length of the output buffer. 121 | -- Choosing a correct size is not straightforward and depends on 122 | -- the variant. The @sodium_base64_ENCODED_LEN(BIN_LEN, VARIANT)@ 123 | -- macro computes the minimum amount of bytes needed to encode BIN_LEN 124 | -- bytes with a chosen VARIANT. 125 | -> Ptr CUChar 126 | -- ^ @bin@, The input buffer. 127 | -> CSize 128 | -- ^ @bin_len@, The length of the input buffer. 129 | -> CInt 130 | -- ^ @variant@, Which Base64 variant to use. None of the variants provide 131 | -- any encryption. 132 | -> IO CString 133 | -- ^ The returned Base64 string, terminated with a null byte. 134 | 135 | {- 136 | foreign import capi "sodium.h sodium_base642bin" 137 | sodiumBase642Bin 138 | :: Ptr CUChar 139 | -- ^ @bin@, The output buffer. 140 | -> CSize 141 | -- ^ @bin_maxlen@, The maximum length of the output buffer. 142 | -> CString 143 | -- ^ @b64@, The input string. 144 | -> CSize 145 | -- ^ @b64_len@, The length of the input. 146 | -> CString 147 | -- ^ @ignore@, a string of characters for the parser to skip. 148 | -- For example, the string ": " allows colons and spaces in the input. 149 | -- These characters will ignored and will not be present in the output. 150 | -> Ptr CSize 151 | -- ^ @bin_len@, The length of the output buffer. 152 | -- This will always be at most @b64_len / 4 * 3@ bytes long. 153 | -> Ptr CString 154 | -- ^ @b64_end@, A pointer to the end of the input string. 155 | -- If this isn't null, then it will be set to the first byte after the last 156 | -- valid parsed character. 157 | -> CInt 158 | -- ^ @variant@, Which Base64 variant to use. None of the variants provide 159 | -- any encryption. 160 | -> IO CInt 161 | -- ^ 0 if successful, -1 on failure. Common failures are if the string 162 | -- couldn't be fully parsed or if the parsed string is longer than the 163 | -- maximum amount of bytes allocated to store it. 164 | -} 165 | 166 | -- | The original variant of Base64 with padding. This ensures that the 167 | -- length of the encoded data will always be a multiple of four bytes. 168 | foreign import capi "sodium.h value sodium_base64_VARIANT_ORIGINAL" 169 | sodiumBase64VariantOriginal :: CInt 170 | 171 | -- | The original variant of Base64. No variant offers any security advantages 172 | -- over the other. 173 | foreign import capi "sodium.h value sodium_base64_VARIANT_ORIGINAL_NO_PADDING" 174 | sodiumBase64VariantOriginalNoPadding :: CInt 175 | 176 | -- | The URL-safe variant of Base64 with padding. 177 | foreign import capi "sodium.h value sodium_base64_VARIANT_URLSAFE" 178 | sodiumBase64VariantURLSafe :: CInt 179 | 180 | -- | The URL-safe variant of Base64. This is the same as the original variant, 181 | -- except '+' and '/' are replaced with '-' and '_'. 182 | foreign import capi "sodium.h value sodium_base64_VARIANT_URLSAFE_NO_PADDING" 183 | sodiumBase64VariantURLSafeNoPadding :: CInt 184 | -------------------------------------------------------------------------------- /libsodium-bindings/src/LibSodium/Bindings/XChaCha20.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CApiFFI #-} 2 | {-# LANGUAGE Trustworthy #-} 3 | 4 | -- | 5 | -- Module: LibSodium.Bindings.XChaCha20 6 | -- Description: Direct bindings to XChaCha20 primitives 7 | -- Copyright: (C) Koz Ross 2022 8 | -- License: BSD-3-Clause 9 | -- Maintainer: koz.ross@retro-freedom.nz 10 | -- Stability: Stable 11 | -- Portability: GHC only 12 | -- 13 | -- Direct bindings to XChaCha20 primitives. 14 | module LibSodium.Bindings.XChaCha20 15 | ( -- * Functions 16 | cryptoStreamXChaCha20 17 | , cryptoStreamXChaCha20Xor 18 | , cryptoStreamXChaCha20XorIC 19 | , cryptoStreamXChaCha20Keygen 20 | 21 | -- * Constants 22 | , cryptoStreamXChaCha20KeyBytes 23 | , cryptoStreamXChaCha20NonceBytes 24 | ) 25 | where 26 | 27 | import Data.Word (Word64) 28 | import Foreign.C.Types 29 | ( CInt (CInt) 30 | , CSize (CSize) 31 | , CUChar 32 | , CULLong (CULLong) 33 | ) 34 | import Foreign.Ptr (Ptr) 35 | 36 | -- | Generate and store a given number of pseudorandom bytes, using a nonce 37 | -- and a secret key. The amount of data read from the nonce location and secret 38 | -- key location will be 'cryptoStreamXChaCha20NonceBytes' and 39 | -- 'cryptoStreamXChaCha20KeyBytes' respectively. 40 | -- 41 | -- This function theoretically returns 0 on success, and -1 on failure. However, 42 | -- [this cannot ever 43 | -- fail](https://github.com/jedisct1/libsodium/discussions/1148#discussioncomment-1979161), 44 | -- although its documentation does not explain this. 45 | -- 46 | -- /See:/ [crypto_stream_xchacha20()](https://doc.libsodium.org/advanced/stream_ciphers/xchacha20#usage) 47 | -- 48 | -- @since 0.0.1.0 49 | foreign import capi "sodium.h crypto_stream_xchacha20" 50 | cryptoStreamXChaCha20 51 | :: Ptr CUChar 52 | -- ^ Out-parameter where pseudorandom bytes will be stored 53 | -> CULLong 54 | -- ^ How many bytes to write 55 | -> Ptr CUChar 56 | -- ^ Nonce location (see documentation, won't be modified) 57 | -> Ptr CUChar 58 | -- ^ Secret key location (see documentation, won't be modified) 59 | -> IO CInt 60 | -- ^ Always 0 (see documentation) 61 | 62 | -- | Encrypt a message of the given length, using a nonce and a secret key. The 63 | -- amount of data read from the nonce location and secret key location will be 64 | -- 'cryptoStreamXChaCha20NonceBytes' and 'cryptoStreamXChaCha20KeyBytes' 65 | -- respectively. 66 | -- 67 | -- The resulting ciphertext does /not/ include an authentication tag. It will be 68 | -- combined with the output of the stream cipher using the XOR operation. 69 | -- 70 | -- This function theoretically returns 0 on success, and -1 on failure. However, 71 | -- [this cannot ever 72 | -- fail](https://github.com/jedisct1/libsodium/discussions/1148#discussioncomment-1979161), 73 | -- although its documentation does not explain this. 74 | -- 75 | -- = Important note 76 | -- 77 | -- The message location and ciphertext location can be the same: this will 78 | -- produce in-place encryption. However, if they are /not/ the same, they must 79 | -- be non-overlapping. 80 | -- 81 | -- /See:/ [crypto_stream_xchacha20_xor()](https://doc.libsodium.org/advanced/stream_ciphers/xchacha20#usage) 82 | -- 83 | -- @since 0.0.1.0 84 | foreign import capi "sodium.h crypto_stream_xchacha20_xor" 85 | cryptoStreamXChaCha20Xor 86 | :: Ptr CUChar 87 | -- ^ Out-parameter where the ciphertext will be stored 88 | -> Ptr CUChar 89 | -- ^ Message location (won't be modified) 90 | -> CULLong 91 | -- ^ Message length 92 | -> Ptr CUChar 93 | -- ^ Nonce location (see documentation, won't be modified) 94 | -> Ptr CUChar 95 | -- ^ Secret key location (see documentation, won't be modified) 96 | -> IO CInt 97 | -- ^ Always 0 (see documentation) 98 | 99 | -- | As 'cryptoStreamXChaCha20Xor', but allows setting the initial value of the 100 | -- block counter to a non-zero value. This permits direct access to any block 101 | -- without having to compute previous ones. 102 | -- 103 | -- See the documentation of 'cryptoStreamXChaCha20Xor' for caveats on the use of 104 | -- this function. 105 | -- 106 | -- /See:/ [crypto_stream_xchacha20_xor_ic()](https://doc.libsodium.org/advanced/stream_ciphers/xchacha20#usage) 107 | -- 108 | -- @since 0.0.1.0 109 | foreign import capi "sodium.h crypto_stream_xchacha20_xor_ic" 110 | cryptoStreamXChaCha20XorIC 111 | :: Ptr CUChar 112 | -- ^ Out-parameter where the ciphertext will be stored 113 | -> Ptr CUChar 114 | -- ^ Message location (won't be modified) 115 | -> CULLong 116 | -- ^ Message length 117 | -> Ptr CUChar 118 | -- ^ Nonce location (see documentation, won't be modified) 119 | -> Word64 120 | -- ^ Value of block counter (see documentation) 121 | -> Ptr CUChar 122 | -- ^ Secret key location (see documentation, won't be modified) 123 | -> IO CInt 124 | -- ^ Always 0 (see documentation) 125 | 126 | -- | Generate a random XChaCha20 secret key. This will always write 127 | -- 'cryptoStreamXChaCha20KeyBytes' to the out-parameter. 128 | -- 129 | -- /See:/ [crypto_stream_xchacha20_keygen()](https://doc.libsodium.org/advanced/stream_ciphers/xchacha20#usage) 130 | -- 131 | -- @since 0.0.1.0 132 | foreign import capi "sodium.h crypto_stream_xchacha20_keygen" 133 | cryptoStreamXChaCha20Keygen 134 | :: Ptr CUChar 135 | -- ^ Out-parameter where the key will be stored 136 | -> IO () 137 | -- ^ Doesn't return anything meaningful 138 | 139 | -- | The number of bytes in an XChaCha20 secret key. 140 | -- 141 | -- /See:/ [crypto_stream_xchacha20_KEYBYTES](https://doc.libsodium.org/advanced/stream_ciphers/xchacha20#constants) 142 | -- 143 | -- @since 0.0.1.0 144 | foreign import capi "sodium.h value crypto_stream_xchacha20_KEYBYTES" 145 | cryptoStreamXChaCha20KeyBytes :: CSize 146 | 147 | -- | The number of bytes in an XChaCha20 nonce. 148 | -- 149 | -- /See:/ [crypto_stream_xchacha20_NONCEBYTES](https://doc.libsodium.org/advanced/stream_ciphers/xchacha20#constants) 150 | -- 151 | -- @since 0.0.1.0 152 | foreign import capi "sodium.h value crypto_stream_xchacha20_NONCEBYTES" 153 | cryptoStreamXChaCha20NonceBytes :: CSize 154 | -------------------------------------------------------------------------------- /sel/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## sel-0.1.0.0 4 | 5 | * Consistently rename ciphertext-related types and functions [#182](https://github.com/haskell-cryptography/libsodium-bindings/pull/182) 6 | * Add support for additional data (AD) in `Sel.SecretKey.Stream` [#183](https://github.com/haskell-cryptography/libsodium-bindings/pull/183) 7 | * Define some cryptographic functions in sel as pure [#189](https://github.com/haskell-cryptography/libsodium-bindings/pull/189) 8 | 9 | ## sel-0.0.3.0 10 | 11 | * Add constant time hex encoding [#176](https://github.com/haskell-cryptography/libsodium-bindings/pull/176) 12 | * Support `text-display` 1.0.0.0 13 | * Replace usages of `memcpy` with `Foreign.copyBytes` [#172](https://github.com/haskell-cryptography/libsodium-bindings/pull/172) 14 | * Add constant-time pointer comparison [#171](https://github.com/haskell-cryptography/libsodium-bindings/pull/171) 15 | * (Internal) Add constant-time Eq, use Scoped for internals [#169](https://github.com/haskell-cryptography/libsodium-bindings/pull/169) 16 | * Cleanup, allow more versions of `tasty` [#168](https://github.com/haskell-cryptography/libsodium-bindings/pull/168) 17 | * (Internal) Add `Scoped` for better readability of nested continuations [#167](https://github.com/haskell-cryptography/libsodium-bindings/pull/167) 18 | * Update hedgehog [#180](https://github.com/haskell-cryptography/libsodium-bindings/pull/180) 19 | 20 | ## sel-0.0.2.0 21 | 22 | * Add usages of `secureMain` in examples 23 | * Depends on libsodium-bindings-0.0.2.0 24 | -------------------------------------------------------------------------------- /sel/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Hécate Moonlight and contributors 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /sel/README.md: -------------------------------------------------------------------------------- 1 | # sel [![CI](https://github.com/haskell-cryptography/libsodium-bindings/actions/workflows/ci.yaml/badge.svg)](https://github.com/haskell-cryptography/libsodium-bindings/actions/workflows/ci.yaml) [![made with Haskell](https://img.shields.io/badge/Made%20in-Haskell-%235e5086?logo=haskell&style=flat-square)](https://haskell.org) 2 | 3 | 4 | Sel is the library for casual users by the [Haskell Cryptography Group](https://haskell-cryptography.org). 5 | It builds on [Libsodium](https://doc.libsodium.org), a reliable and audited library for common operations. 6 | 7 | ## Hashing 8 | 9 | | Purpose | Module | 10 | |----------------------------------------------------------------------|--------------------------------| 11 | | Hash passwords | [Sel.Hashing.Password](https://hackage.haskell.org/package/sel-0.0.1.0/candidate/docs/Sel-Hashing-Password.html) | 12 | | Verify the integrity of files and hash large data | [Sel.Hashing](https://hackage.haskell.org/package/sel-0.0.1.0/candidate/docs/Sel-Hashing.html) | 13 | | Hash tables, bloom filters, fast integrity checking of short input | [Sel.Hashing.Short](https://hackage.haskell.org/package/sel-0.0.1.0/candidate/docs/Sel-Hashing-Short.html) | 14 | 15 | ## Secret key / Symmetric cryptography 16 | 17 | | Purpose | Module | 18 | |----------------------------------------------------------------------|--------------------------------| 19 | | Authenticate a message with a secret key | [Sel.SecretKey.Authentication](https://hackage.haskell.org/package/sel-0.0.1.0/candidate/docs/Sel-SecretKey-Authentication.html) | 20 | | Encrypt and sign data with a secret key | [Sel.SecretKey.Cipher](https://hackage.haskell.org/package/sel-0.0.1.0/candidate/docs/Sel-SecretKey-Cipher.html) | 21 | | Encrypt a stream of messages | [Sel.SecretKey.Stream](https://hackage.haskell.org/package/sel-0.0.1.0/candidate/docs/Sel-SecretKey-Stream.html) | 22 | 23 | ## Public and Secret key / Asymmetric cryptography 24 | 25 | | Purpose | Module | 26 | |----------------------------------------------------------------------|--------------------------------| 27 | | Sign and encrypt with my secret key and my recipient's public key | [Sel.PublicKey.Cipher](https://hackage.haskell.org/package/sel-0.0.1.0/candidate/docs/Sel-PublicKey-Cipher.html) | 28 | | Sign and encrypt an anonymous message with my recipient's public key | [Sel.PublicKey.Seal](https://hackage.haskell.org/package/sel-0.0.1.0/candidate/docs/Sel-PublicKey-Seal.html) | 29 | | Sign with a secret key and distribute my public key | [Sel.PublicKey.Signature](https://hackage.haskell.org/package/sel-0.0.1.0/candidate/docs/Sel-PublicKey-Signature.html) | 30 | 31 | ## HMAC message authentication 32 | 33 | | Purpose | Module | 34 | |----------------------------------------------------------------------|--------------------------------| 35 | | HMAC-256 | [Sel.HMAC.SHA256](https://hackage.haskell.org/package/sel-0.0.1.0/candidate/docs/Sel-HMAC-SHA256.html) | 36 | | HMAC-512 | [Sel.HMAC.SHA512](https://hackage.haskell.org/package/sel-0.0.1.0/candidate/docs/Sel-HMAC-SHA512.html) | 37 | | HMAC-512-256 | [Sel.HMAC.SHA512_256](https://hackage.haskell.org/package/sel-0.0.1.0/candidate/docs/Sel-HMAC-SHA512_256.html) | 38 | 39 | ## Legacy SHA2 constructs 40 | 41 | | Purpose | Module | 42 | |----------------------------------------------------------------------|--------------------------------| 43 | | SHA-256 | [Sel.Hashing.SHA256](https://hackage.haskell.org/package/sel-0.0.1.0/candidate/docs/Sel-Hashing-SHA256.html) | 44 | | SHA-512 | [Sel.Hashing.SHA512](https://hackage.haskell.org/package/sel-0.0.1.0/candidate/docs/Sel-Hashing-SHA512.html) | 45 | | Scrypt | [Sel.Scrypt](https://hackage.haskell.org/package/sel-0.0.1.0/candidate/docs/Sel-Scrypt.html) | 46 | -------------------------------------------------------------------------------- /sel/sel.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 3.0 2 | name: sel 3 | version: 0.1.0.0 4 | category: Cryptography 5 | synopsis: Cryptography for the casual user 6 | description: 7 | The high-level library aimed at casual users of cryptography, by the Haskell Cryptography Group 8 | 9 | homepage: https://github.com/haskell-cryptography/libsodium-bindings 10 | bug-reports: 11 | https://github.com/haskell-cryptography/libsodium-bindings/issues 12 | 13 | author: Hécate Moonlight, Koz Ross 14 | maintainer: The Haskell Cryptography contributors 15 | license: BSD-3-Clause 16 | build-type: Simple 17 | tested-with: 18 | GHC ==9.2.8 || ==9.4.8 || ==9.6.6 || ==9.8.2 || ==9.10.1 || ==9.12.1 19 | 20 | extra-source-files: 21 | LICENSE 22 | README.md 23 | 24 | extra-doc-files: CHANGELOG.md 25 | 26 | source-repository head 27 | type: git 28 | location: https://github.com/haskell-cryptography/libsodium-bindings 29 | subdir: sel 30 | 31 | common common 32 | ghc-options: 33 | -Wall 34 | -Wcompat 35 | -Widentities 36 | -Wincomplete-record-updates 37 | -Wincomplete-uni-patterns 38 | -Wpartial-fields 39 | -Wredundant-constraints 40 | -fhide-source-paths 41 | -Wno-unused-do-bind 42 | -fshow-hole-constraints 43 | -fprint-potential-instances 44 | -Wno-unticked-promoted-constructors 45 | -Werror=unused-imports 46 | 47 | default-language: Haskell2010 48 | 49 | common test-options 50 | ghc-options: 51 | -rtsopts 52 | -threaded 53 | -with-rtsopts=-N 54 | 55 | library 56 | import: common 57 | hs-source-dirs: src 58 | exposed-modules: 59 | Sel 60 | Sel.HMAC 61 | Sel.HMAC.SHA256 62 | Sel.HMAC.SHA512 63 | Sel.HMAC.SHA512_256 64 | Sel.Hashing 65 | Sel.Hashing.Password 66 | Sel.Hashing.SHA256 67 | Sel.Hashing.SHA512 68 | Sel.Hashing.Short 69 | Sel.PublicKey.Cipher 70 | Sel.PublicKey.Seal 71 | Sel.PublicKey.Signature 72 | Sel.Scrypt 73 | Sel.SecretKey.Authentication 74 | Sel.SecretKey.Cipher 75 | Sel.SecretKey.Stream 76 | 77 | other-modules: 78 | Sel.Internal 79 | Sel.Internal.Scoped 80 | Sel.Internal.Scoped.Foreign 81 | Sel.Internal.Sodium 82 | 83 | build-depends: 84 | base >=4.14 && <5, 85 | base16 ^>=1.0, 86 | bytestring >=0.10 && <0.13, 87 | libsodium-bindings ^>=0.0.3, 88 | text >=1.2 && <2.2, 89 | text-builder-linear ^>=0.1, 90 | text-display ^>=1.0, 91 | transformers ^>=0.6.0, 92 | 93 | test-suite sel-tests 94 | import: common 95 | import: test-options 96 | type: exitcode-stdio-1.0 97 | main-is: Main.hs 98 | other-modules: 99 | Test.HMAC 100 | Test.Hashing 101 | Test.Hashing.Password 102 | Test.Hashing.SHA2 103 | Test.Hashing.Short 104 | Test.PublicKey.Cipher 105 | Test.PublicKey.Seal 106 | Test.PublicKey.Signature 107 | Test.Scrypt 108 | Test.SecretKey.Authentication 109 | Test.SecretKey.Cipher 110 | Test.SecretKey.Stream 111 | TestUtils 112 | 113 | hs-source-dirs: test 114 | build-depends: 115 | base, 116 | base16, 117 | bytestring, 118 | hedgehog >=1.4, 119 | libsodium-bindings, 120 | sel, 121 | tasty >=1.4 && <1.6, 122 | tasty-hunit ^>=0.10, 123 | text, 124 | text-display, 125 | -------------------------------------------------------------------------------- /sel/src/Sel.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- 3 | -- Module: Sel 4 | -- Description: Cryptography for the casual user 5 | -- License: BSD-3-Clause 6 | -- Maintainer: The Haskell Cryptography Group 7 | -- Portability: GHC only 8 | -- 9 | -- Sel is the library for casual users by the [Haskell Cryptography Group](https://haskell-cryptography.org). 10 | -- 11 | -- It builds on [Libsodium](https://doc.libsodium.org), a reliable and audited library for common operations. 12 | -- 13 | -- ⚠️ Important note: if you want to use any of this code in an executable, ensure that you use 'secureMain' or 'secureMainWithError' 14 | -- in your @main@ function __before__ you call any functions from this library. Failing to do so will cause problems. For libraries, this is not necessary. 15 | module Sel 16 | ( -- ** Available APIs 17 | -- $tableofcontent 18 | 19 | -- ** Program entrypoint 20 | secureMain 21 | , secureMainWithError 22 | ) where 23 | 24 | import LibSodium.Bindings.Main (secureMain, secureMainWithError) 25 | 26 | -- $tableofcontent 27 | -- === Hashing 28 | -- +----------------------------------------------------------------------+--------------------------------+ 29 | -- | Purpose | Module | 30 | -- +======================================================================+================================+ 31 | -- | Hash passwords | "Sel.Hashing.Password" | 32 | -- +----------------------------------------------------------------------+--------------------------------+ 33 | -- | Verify the integrity of files and hash large data | "Sel.Hashing" | 34 | -- +----------------------------------------------------------------------+--------------------------------+ 35 | -- | Hash tables, bloom filters, fast integrity checking of short input | "Sel.Hashing.Short" | 36 | -- +----------------------------------------------------------------------+--------------------------------+ 37 | -- 38 | -- === Secret key / symmetric cryptography 39 | -- +----------------------------------------------------------------------+--------------------------------+ 40 | -- | Purpose | Module | 41 | -- +======================================================================+================================+ 42 | -- | Authenticate a message with a secret key | "Sel.SecretKey.Authentication" | 43 | -- +----------------------------------------------------------------------+--------------------------------+ 44 | -- | Encrypt and sign data with a secret key | "Sel.SecretKey.Cipher" | 45 | -- +----------------------------------------------------------------------+--------------------------------+ 46 | -- | Encrypt a stream of messages | "Sel.SecretKey.Stream" | 47 | -- +----------------------------------------------------------------------+--------------------------------+ 48 | -- 49 | -- 50 | -- === Public and Secret key / asymmetric cryptography 51 | -- +----------------------------------------------------------------------+--------------------------------+ 52 | -- | Purpose | Module | 53 | -- +======================================================================+================================+ 54 | -- | Sign and encrypt with my secret key and my recipient's public key | "Sel.PublicKey.Cipher" | 55 | -- +----------------------------------------------------------------------+--------------------------------+ 56 | -- | Sign and encrypt an anonymous message with my recipient's public key | "Sel.PublicKey.Seal" | 57 | -- +----------------------------------------------------------------------+--------------------------------+ 58 | -- | Sign with a secret key and distribute my public key | "Sel.PublicKey.Signature" | 59 | -- +----------------------------------------------------------------------+--------------------------------+ 60 | -- 61 | -- === HMAC message authentication 62 | -- +----------------------------------------------------------------------+--------------------------------+ 63 | -- | Purpose | Module | 64 | -- +======================================================================+================================+ 65 | -- | HMAC-256 | "Sel.HMAC.SHA256" | 66 | -- +----------------------------------------------------------------------+--------------------------------+ 67 | -- | HMAC-512 | "Sel.HMAC.SHA512" | 68 | -- +----------------------------------------------------------------------+--------------------------------+ 69 | -- | HMAC-512-256 | "Sel.HMAC.SHA512_256" | 70 | -- +----------------------------------------------------------------------+--------------------------------+ 71 | -- 72 | -- === Legacy constructs 73 | -- +----------------------------------------------------------------------+--------------------------------+ 74 | -- | Purpose | Module | 75 | -- +======================================================================+================================+ 76 | -- | SHA-256 | "Sel.Hashing.SHA256" | 77 | -- +----------------------------------------------------------------------+--------------------------------+ 78 | -- | SHA-512 | "Sel.Hashing.SHA512" | 79 | -- +----------------------------------------------------------------------+--------------------------------+ 80 | -- | Scrypt | "Sel.Scrypt" | 81 | -- +----------------------------------------------------------------------+--------------------------------+ 82 | -------------------------------------------------------------------------------- /sel/src/Sel/HMAC.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- 3 | -- Module: Sel.HMAC.SHA512 4 | -- Description: Keyed Message Authentication Codes 5 | -- 6 | -- [HMAC](https://en.wikipedia.org/wiki/HMAC) provides a way to both encrypt a communication 7 | -- and authenticate its origin. 8 | -- 9 | -- This relies on a shared pair of secret keys between all the parties 10 | -- 11 | -- The function computing the tag deterministic: the same @(message, key)@ tuple will always 12 | -- produce the same output. However, even if the message is public, knowing the key is required 13 | -- in order to be able to compute a valid tag. 14 | -- Therefore, the key should remain confidential. The tag, however, can be public. 15 | -- 16 | -- The following keyed message authentication codes are availabled: 17 | -- 18 | -- * "Sel.HMAC.SHA256" 19 | -- * "Sel.HMAC.SHA512" 20 | -- * "Sel.HMAC.SHA512_256" (truncated HMAC-SHA-512) 21 | module Sel.HMAC where 22 | -------------------------------------------------------------------------------- /sel/src/Sel/Hashing/SHA256.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE InstanceSigs #-} 2 | {-# LANGUAGE KindSignatures #-} 3 | {-# LANGUAGE RankNTypes #-} 4 | {-# LANGUAGE RoleAnnotations #-} 5 | {-# LANGUAGE ScopedTypeVariables #-} 6 | {-# LANGUAGE TypeApplications #-} 7 | 8 | -- | 9 | -- Module: Sel.Hashing.SHA256 10 | -- Description: Legacy SHA-256 hashing 11 | -- Copyright: (C) Hécate Moonlight 2022 12 | -- License: BSD-3-Clause 13 | -- Maintainer: The Haskell Cryptography Group 14 | -- Portability: GHC only 15 | module Sel.Hashing.SHA256 16 | ( -- ** Usage 17 | -- $usage 18 | Hash 19 | 20 | -- ** Hashing a single message 21 | , hashByteString 22 | , hashText 23 | 24 | -- ** Hashing a multi-part message 25 | , Multipart 26 | , withMultipart 27 | , updateMultipart 28 | 29 | -- ** Displaying 30 | , hashToBinary 31 | , hashToHexText 32 | , hashToHexByteString 33 | ) where 34 | 35 | import Control.Monad (void) 36 | import Control.Monad.IO.Class (MonadIO (liftIO)) 37 | import qualified Data.Base16.Types as Base16 38 | import Data.ByteString (StrictByteString) 39 | import qualified Data.ByteString.Base16 as Base16 40 | import qualified Data.ByteString.Internal as BS 41 | import qualified Data.ByteString.Unsafe as BS 42 | import Data.Kind (Type) 43 | import Data.Text (Text) 44 | import qualified Data.Text.Builder.Linear as Builder 45 | import Data.Text.Display (Display (..)) 46 | import qualified Data.Text.Encoding as Text 47 | import Foreign (ForeignPtr, Ptr, Storable) 48 | import qualified Foreign 49 | import Foreign.C (CChar, CSize, CUChar, CULLong) 50 | import LibSodium.Bindings.SHA2 51 | ( CryptoHashSHA256State 52 | , cryptoHashSHA256 53 | , cryptoHashSHA256Bytes 54 | , cryptoHashSHA256Final 55 | , cryptoHashSHA256Init 56 | , cryptoHashSHA256StateBytes 57 | , cryptoHashSHA256Update 58 | ) 59 | import System.IO.Unsafe (unsafeDupablePerformIO) 60 | 61 | import Sel.Internal 62 | import Sel.Internal.Sodium (binaryToHex) 63 | 64 | -- $usage 65 | -- 66 | -- The SHA-2 family of hashing functions is only provided for interoperability with other applications. 67 | -- 68 | -- If you are looking for a generic hash function, do use 'Sel.Hashing'. 69 | -- 70 | -- If you are looking to hash passwords or deriving keys from passwords, do use 'Sel.Hashing.Password', 71 | -- as the functions of the SHA-2 family are not suitable for this task. 72 | -- 73 | -- Only import this module qualified like this: 74 | -- 75 | -- >>> import qualified Sel.Hashing.SHA256 as SHA256 76 | 77 | -- | A hashed value from the SHA-256 algorithm. 78 | -- 79 | -- @since 0.0.1.0 80 | newtype Hash = Hash (ForeignPtr CUChar) 81 | 82 | -- | 83 | -- 84 | -- @since 0.0.1.0 85 | instance Eq Hash where 86 | (Hash h1) == (Hash h2) = 87 | foreignPtrEq h1 h2 cryptoHashSHA256Bytes 88 | 89 | -- | 90 | -- 91 | -- @since 0.0.1.0 92 | instance Ord Hash where 93 | compare (Hash h1) (Hash h2) = 94 | foreignPtrOrd h1 h2 cryptoHashSHA256Bytes 95 | 96 | -- | 97 | -- 98 | -- @since 0.0.1.0 99 | instance Storable Hash where 100 | sizeOf :: Hash -> Int 101 | sizeOf _ = fromIntegral cryptoHashSHA256Bytes 102 | 103 | -- Aligned on the size of 'cryptoHashSHA256Bytes' 104 | alignment :: Hash -> Int 105 | alignment _ = 32 106 | 107 | poke :: Ptr Hash -> Hash -> IO () 108 | poke ptr (Hash hashForeignPtr) = 109 | Foreign.withForeignPtr hashForeignPtr $ \hashPtr -> 110 | Foreign.copyArray (Foreign.castPtr ptr) hashPtr (fromIntegral cryptoHashSHA256Bytes) 111 | 112 | peek :: Ptr Hash -> IO Hash 113 | peek ptr = do 114 | hashfPtr <- Foreign.mallocForeignPtrBytes (fromIntegral cryptoHashSHA256Bytes) 115 | Foreign.withForeignPtr hashfPtr $ \hashPtr -> 116 | Foreign.copyArray hashPtr (Foreign.castPtr ptr) (fromIntegral cryptoHashSHA256Bytes) 117 | pure $ Hash hashfPtr 118 | 119 | -- | 120 | -- 121 | -- @since 0.0.1.0 122 | instance Display Hash where 123 | displayBuilder = Builder.fromText . hashToHexText 124 | 125 | -- | 126 | -- 127 | -- @since 0.0.1.0 128 | instance Show Hash where 129 | show = BS.unpackChars . hashToHexByteString 130 | 131 | -- | Hash a 'StrictByteString' with the SHA-256 algorithm. 132 | -- 133 | -- @since 0.0.1.0 134 | hashByteString :: StrictByteString -> Hash 135 | hashByteString bytestring = unsafeDupablePerformIO $ 136 | BS.unsafeUseAsCStringLen bytestring $ \(cString, cStringLen) -> do 137 | hashForeignPtr <- Foreign.mallocForeignPtrBytes (fromIntegral cryptoHashSHA256Bytes) 138 | Foreign.withForeignPtr hashForeignPtr $ \hashPtr -> 139 | void $ 140 | cryptoHashSHA256 141 | hashPtr 142 | (Foreign.castPtr cString :: Ptr CUChar) 143 | (fromIntegral @Int @CULLong cStringLen) 144 | pure $ Hash hashForeignPtr 145 | 146 | -- | Hash a UTF8-encoded strict 'Text' with the SHA-256 algorithm. 147 | -- 148 | -- @since 0.0.1.0 149 | hashText :: Text -> Hash 150 | hashText text = hashByteString (Text.encodeUtf8 text) 151 | 152 | -- == Displaying 153 | 154 | -- | Convert a 'Hash' to a strict hexadecimal 'Text'. 155 | -- 156 | -- @since 0.0.1.0 157 | hashToHexText :: Hash -> Text 158 | hashToHexText = Base16.extractBase16 . Base16.encodeBase16 . hashToBinary 159 | 160 | -- | Convert a 'Hash' to a strict, hexadecimal-encoded 'StrictByteString' in constant time. 161 | -- 162 | -- @since 0.0.1.0 163 | hashToHexByteString :: Hash -> StrictByteString 164 | hashToHexByteString (Hash hashForeignPtr) = 165 | binaryToHex hashForeignPtr cryptoHashSHA256Bytes 166 | 167 | -- | Convert a 'Hash' to a binary 'StrictByteString'. 168 | -- 169 | -- @since 0.0.1.0 170 | hashToBinary :: Hash -> StrictByteString 171 | hashToBinary (Hash fPtr) = 172 | BS.fromForeignPtr 173 | (Foreign.castForeignPtr fPtr) 174 | 0 175 | (fromIntegral @CSize @Int cryptoHashSHA256Bytes) 176 | 177 | -- ** Hashing a multi-part message 178 | 179 | -- | 'Multipart' is a cryptographic context for streaming hashing. 180 | -- This API can be used when a message is too big to fit in memory or when the message is received in portions. 181 | -- 182 | -- Use it like this: 183 | -- 184 | -- >>> hash <- SHA256.withMultipart $ \multipartState -> do -- we are in MonadIO 185 | -- ... message1 <- getMessage 186 | -- ... SHA256.updateMultipart multipartState message1 187 | -- ... message2 <- getMessage 188 | -- ... SHA256.updateMultipart multipartState message2 189 | -- 190 | -- @since 0.0.1.0 191 | newtype Multipart s = Multipart (Ptr CryptoHashSHA256State) 192 | 193 | type role Multipart nominal 194 | 195 | -- | Perform streaming hashing with a 'Multipart' cryptographic context. 196 | -- 197 | -- Use 'SHA256.updateMultipart' within the continuation. 198 | -- 199 | -- The context is safely allocated first, then the continuation is run 200 | -- and then it is deallocated after that. 201 | -- 202 | -- @since 0.0.1.0 203 | withMultipart 204 | :: forall (a :: Type) (m :: Type -> Type) 205 | . MonadIO m 206 | => (forall s. Multipart s -> m a) 207 | -- ^ Continuation that gives you access to a 'Multipart' cryptographic context 208 | -> m Hash 209 | withMultipart actions = do 210 | allocateWith cryptoHashSHA256StateBytes $ \statePtr -> do 211 | void $ liftIO $ cryptoHashSHA256Init statePtr 212 | let part = Multipart statePtr 213 | actions part 214 | liftIO (finaliseMultipart part) 215 | 216 | -- | Compute the 'Hash' of all the portions that were fed to the cryptographic context. 217 | -- 218 | -- this function is only used within 'withMultipart' 219 | -- 220 | -- @since 0.0.1.0 221 | finaliseMultipart :: Multipart s -> IO Hash 222 | finaliseMultipart (Multipart statePtr) = do 223 | hashForeignPtr <- Foreign.mallocForeignPtrBytes (fromIntegral cryptoHashSHA256Bytes) 224 | Foreign.withForeignPtr hashForeignPtr $ \(hashPtr :: Ptr CUChar) -> 225 | void $ 226 | cryptoHashSHA256Final 227 | statePtr 228 | hashPtr 229 | pure $ Hash hashForeignPtr 230 | 231 | -- | Add a message portion to be hashed. 232 | -- 233 | -- This function should be used within 'withMultipart'. 234 | -- 235 | -- @since 0.0.1.0 236 | updateMultipart :: Multipart s -> StrictByteString -> IO () 237 | updateMultipart (Multipart statePtr) message = do 238 | BS.unsafeUseAsCStringLen message $ \(cString, cStringLen) -> do 239 | let messagePtr = Foreign.castPtr @CChar @CUChar cString 240 | let messageLen = fromIntegral @Int @CULLong cStringLen 241 | void $ 242 | cryptoHashSHA256Update 243 | statePtr 244 | messagePtr 245 | messageLen 246 | -------------------------------------------------------------------------------- /sel/src/Sel/Hashing/SHA512.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE InstanceSigs #-} 2 | {-# LANGUAGE KindSignatures #-} 3 | {-# LANGUAGE RankNTypes #-} 4 | {-# LANGUAGE RoleAnnotations #-} 5 | {-# LANGUAGE ScopedTypeVariables #-} 6 | {-# LANGUAGE TypeApplications #-} 7 | 8 | -- | 9 | -- Module: Sel.Hashing.SHA512 10 | -- Description: Legacy SHA-512 hashing 11 | -- Copyright: (C) Hécate Moonlight 2022 12 | -- License: BSD-3-Clause 13 | -- Maintainer: The Haskell Cryptography Group 14 | -- Portability: GHC only 15 | module Sel.Hashing.SHA512 16 | ( -- ** Usage 17 | -- $usage 18 | 19 | -- ** Hash 20 | Hash 21 | , hashToBinary 22 | , hashToHexText 23 | , hashToHexByteString 24 | 25 | -- ** Hashing a single message 26 | , hashByteString 27 | , hashText 28 | 29 | -- ** Hashing a multi-parts message 30 | , Multipart 31 | , withMultipart 32 | , updateMultipart 33 | ) where 34 | 35 | import Control.Monad (void) 36 | import Control.Monad.IO.Class (MonadIO, liftIO) 37 | import qualified Data.Base16.Types as Base16 38 | import Data.ByteString (StrictByteString) 39 | import qualified Data.ByteString.Base16 as Base16 40 | import qualified Data.ByteString.Internal as BS 41 | import qualified Data.ByteString.Unsafe as BS 42 | import Data.Kind (Type) 43 | import Data.Text (Text) 44 | import qualified Data.Text.Builder.Linear as Builder 45 | import Data.Text.Display (Display (..)) 46 | import qualified Data.Text.Encoding as Text 47 | import Foreign (ForeignPtr, Ptr, Storable) 48 | import qualified Foreign 49 | import Foreign.C (CChar, CSize, CUChar, CULLong) 50 | import LibSodium.Bindings.SHA2 51 | ( CryptoHashSHA512State 52 | , cryptoHashSHA512 53 | , cryptoHashSHA512Bytes 54 | , cryptoHashSHA512Final 55 | , cryptoHashSHA512Init 56 | , cryptoHashSHA512StateBytes 57 | , cryptoHashSHA512Update 58 | ) 59 | import System.IO.Unsafe (unsafeDupablePerformIO) 60 | 61 | import Sel.Internal 62 | import Sel.Internal.Sodium (binaryToHex) 63 | 64 | -- $usage 65 | -- 66 | -- The SHA-2 family of hashing functions is only provided for interoperability with other applications. 67 | -- 68 | -- If you are looking for a generic hash function, do use 'Sel.Hashing'. 69 | -- 70 | -- If you are looking to hash passwords or deriving keys from passwords, do use 'Sel.Hashing.Password', 71 | -- as the functions of the SHA-2 family are not suitable for this task. 72 | -- 73 | -- Only import this module qualified like this: 74 | -- 75 | -- >>> import qualified Sel.Hashing.SHA512 as SHA512 76 | 77 | -- | A hashed value from the SHA-512 algorithm. 78 | -- 79 | -- @since 0.0.1.0 80 | newtype Hash = Hash (ForeignPtr CUChar) 81 | 82 | -- | 83 | -- 84 | -- @since 0.0.1.0 85 | instance Eq Hash where 86 | (Hash h1) == (Hash h2) = 87 | foreignPtrEq h1 h2 cryptoHashSHA512Bytes 88 | 89 | -- | 90 | -- 91 | -- @since 0.0.1.0 92 | instance Ord Hash where 93 | compare (Hash h1) (Hash h2) = 94 | foreignPtrOrd h1 h2 cryptoHashSHA512Bytes 95 | 96 | -- | 97 | -- 98 | -- @since 0.0.1.0 99 | instance Storable Hash where 100 | sizeOf :: Hash -> Int 101 | sizeOf _ = fromIntegral cryptoHashSHA512Bytes 102 | 103 | -- Aligned on the size of 'cryptoHashSHA512Bytes' 104 | alignment :: Hash -> Int 105 | alignment _ = 32 106 | 107 | poke :: Ptr Hash -> Hash -> IO () 108 | poke ptr (Hash hashForeignPtr) = 109 | Foreign.withForeignPtr hashForeignPtr $ \hashPtr -> 110 | Foreign.copyArray (Foreign.castPtr ptr) hashPtr (fromIntegral cryptoHashSHA512Bytes) 111 | 112 | peek :: Ptr Hash -> IO Hash 113 | peek ptr = do 114 | hashfPtr <- Foreign.mallocForeignPtrBytes (fromIntegral cryptoHashSHA512Bytes) 115 | Foreign.withForeignPtr hashfPtr $ \hashPtr -> 116 | Foreign.copyArray hashPtr (Foreign.castPtr ptr) (fromIntegral cryptoHashSHA512Bytes) 117 | pure $ Hash hashfPtr 118 | 119 | -- | 120 | -- 121 | -- @since 0.0.1.0 122 | instance Display Hash where 123 | displayBuilder = Builder.fromText . hashToHexText 124 | 125 | -- | 126 | -- 127 | -- @since 0.0.1.0 128 | instance Show Hash where 129 | show = BS.unpackChars . hashToHexByteString 130 | 131 | -- ** Hashing a single message 132 | 133 | -- | Convert a 'Hash' to a strict hexadecimal 'Text'. 134 | -- 135 | -- @since 0.0.1.0 136 | hashToHexText :: Hash -> Text 137 | hashToHexText = Base16.extractBase16 . Base16.encodeBase16 . hashToBinary 138 | 139 | -- | Convert a 'Hash' to a strict, hexadecimal-encoded 'StrictByteString' in constant time. 140 | -- 141 | -- @since 0.0.1.0 142 | hashToHexByteString :: Hash -> StrictByteString 143 | hashToHexByteString (Hash hashForeignPtr) = 144 | binaryToHex hashForeignPtr cryptoHashSHA512Bytes 145 | 146 | -- | Convert a 'Hash' to a binary 'StrictByteString'. 147 | -- 148 | -- @since 0.0.1.0 149 | hashToBinary :: Hash -> StrictByteString 150 | hashToBinary (Hash fPtr) = 151 | BS.fromForeignPtr 152 | (Foreign.castForeignPtr fPtr) 153 | 0 154 | (fromIntegral @CSize @Int cryptoHashSHA512Bytes) 155 | 156 | -- | Hash a 'StrictByteString' with the SHA-512 algorithm. 157 | -- 158 | -- @since 0.0.1.0 159 | hashByteString :: StrictByteString -> Hash 160 | hashByteString bytestring = unsafeDupablePerformIO $ 161 | BS.unsafeUseAsCStringLen bytestring $ \(cString, cStringLen) -> do 162 | hashForeignPtr <- Foreign.mallocForeignPtrBytes (fromIntegral cryptoHashSHA512Bytes) 163 | Foreign.withForeignPtr hashForeignPtr $ \hashPtr -> 164 | void $ 165 | cryptoHashSHA512 166 | hashPtr 167 | (Foreign.castPtr cString :: Ptr CUChar) 168 | (fromIntegral @Int @CULLong cStringLen) 169 | pure $ Hash hashForeignPtr 170 | 171 | -- | Hash a UTF8-encoded strict 'Text' with the SHA-512 algorithm. 172 | -- 173 | -- @since 0.0.1.0 174 | hashText :: Text -> Hash 175 | hashText text = hashByteString (Text.encodeUtf8 text) 176 | 177 | -- ** Hashing a multi-parts message 178 | 179 | -- | 'Multipart' is a cryptographic context for streaming hashing. 180 | -- This API can be used when a message is too big to fit in memory or when the message is received in portions. 181 | -- 182 | -- Use it like this: 183 | -- 184 | -- >>> hash <- SHA512.withMultipart $ \multipartState -> do -- we are in MonadIO 185 | -- ... message1 <- getMessage 186 | -- ... SHA512.updateMultipart multipartState message1 187 | -- ... message2 <- getMessage 188 | -- ... SHA512.updateMultipart multipartState message2 189 | -- 190 | -- @since 0.0.1.0 191 | newtype Multipart s = Multipart (Ptr CryptoHashSHA512State) 192 | 193 | type role Multipart nominal 194 | 195 | -- | Perform streaming hashing with a 'Multipart' cryptographic context. 196 | -- 197 | -- Use 'SHA512.updateMultipart' and 'SHA512.finaliseMultipart' inside of the continuation. 198 | -- 199 | -- The context is safely allocated and deallocated inside of the continuation. 200 | -- 201 | -- @since 0.0.1.0 202 | withMultipart 203 | :: forall (a :: Type) (m :: Type -> Type) 204 | . MonadIO m 205 | => (forall s. Multipart s -> m a) 206 | -- ^ Continuation that gives you access to a 'Multipart' cryptographic context 207 | -> m Hash 208 | withMultipart action = do 209 | allocateWith cryptoHashSHA512StateBytes $ \statePtr -> do 210 | void $ liftIO $ cryptoHashSHA512Init statePtr 211 | let part = Multipart statePtr 212 | action part 213 | liftIO $ finaliseMultipart part 214 | 215 | -- | Compute the 'Hash' of all the portions that were fed to the cryptographic context. 216 | -- 217 | -- This function is only used within 'withMultipart'. 218 | -- 219 | -- @since 0.0.1.0 220 | finaliseMultipart :: Multipart s -> IO Hash 221 | finaliseMultipart (Multipart statePtr) = do 222 | hashForeignPtr <- Foreign.mallocForeignPtrBytes (fromIntegral cryptoHashSHA512Bytes) 223 | Foreign.withForeignPtr hashForeignPtr $ \(hashPtr :: Ptr CUChar) -> 224 | void $ 225 | cryptoHashSHA512Final 226 | statePtr 227 | hashPtr 228 | pure $ Hash hashForeignPtr 229 | 230 | -- | Add a message portion to be hashed. 231 | -- 232 | -- This function should be used within 'withMultipart'. 233 | -- 234 | -- @since 0.0.1.0 235 | updateMultipart :: Multipart s -> StrictByteString -> IO () 236 | updateMultipart (Multipart statePtr) message = do 237 | BS.unsafeUseAsCStringLen message $ \(cString, cStringLen) -> do 238 | let messagePtr = Foreign.castPtr @CChar @CUChar cString 239 | let messageLen = fromIntegral @Int @CULLong cStringLen 240 | void $ 241 | cryptoHashSHA512Update 242 | statePtr 243 | messagePtr 244 | messageLen 245 | -------------------------------------------------------------------------------- /sel/src/Sel/Hashing/Short.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DeriveAnyClass #-} 2 | {-# LANGUAGE DerivingVia #-} 3 | {-# LANGUAGE TypeApplications #-} 4 | 5 | -- | 6 | -- 7 | -- Module: Sel.Hashing.Short 8 | -- Description: Short input hashing with the SipHash-2-4 algorithm 9 | -- Copyright: (C) Hécate Moonlight 2023 10 | -- License: BSD-3-Clause 11 | -- Maintainer: The Haskell Cryptography Group 12 | -- Portability: GHC only 13 | module Sel.Hashing.Short 14 | ( -- ** Introduction 15 | -- $introduction 16 | ShortHash 17 | 18 | -- ** Short-input Hashing 19 | , hashByteString 20 | , hashText 21 | 22 | -- *** Conversion 23 | , shortHashToBinary 24 | , shortHashToHexText 25 | , shortHashToHexByteString 26 | 27 | -- ** Short Hash Key 28 | , ShortHashKey 29 | , newKey 30 | 31 | -- *** Conversion 32 | , shortHashKeyToBinary 33 | , shortHashKeyToHexText 34 | , shortHashKeyToHexByteString 35 | , binaryToShortHashKey 36 | , hexTextToShortHashKey 37 | , hexByteStringToShortHashKey 38 | 39 | -- ** Errors 40 | , ShortHashingException (..) 41 | ) 42 | where 43 | 44 | import Control.Exception (throw) 45 | import Control.Monad (void, when) 46 | import qualified Data.Base16.Types as Base16 47 | import Data.ByteString (StrictByteString) 48 | import qualified Data.ByteString as BS 49 | import qualified Data.ByteString.Base16 as Base16 50 | import qualified Data.ByteString.Internal as BS 51 | import qualified Data.ByteString.Unsafe as BS 52 | import Data.Text (Text) 53 | import qualified Data.Text as Text 54 | import qualified Data.Text.Builder.Linear as Builder 55 | import Data.Text.Display 56 | import qualified Data.Text.Encoding as Text 57 | import Foreign hiding (void) 58 | import Foreign.C (CChar, CSize, CUChar, CULLong) 59 | import GHC.Exception (Exception) 60 | import LibSodium.Bindings.ShortHashing 61 | ( cryptoShortHashSipHashX24Bytes 62 | , cryptoShortHashSipHashX24KeyBytes 63 | , cryptoShortHashX24 64 | , cryptoShortHashX24KeyGen 65 | ) 66 | import System.IO.Unsafe (unsafeDupablePerformIO) 67 | 68 | import Sel.Internal 69 | import Sel.Internal.Sodium (binaryToHex) 70 | 71 | -- $introduction 72 | -- 73 | -- This module provides an API for performant short-input hashing, 74 | -- backed by the [SipHash-2-4](https://en.wikipedia.org/wiki/SipHash) algorithm. 75 | -- 76 | -- Short-input hashing functions have a variety of use-cases, such as: 77 | -- 78 | -- * Hash Tables 79 | -- * Probabilistic data structures, such as Bloom filters 80 | -- * Integrity checking in interactive protocols 81 | 82 | -- | A 128-bit hash of a short input, of size 'cryptoShortHashSipHashX24Bytes' 83 | -- 84 | -- @since 0.0.1.0 85 | newtype ShortHash = ShortHash (ForeignPtr CUChar) 86 | 87 | -- | 88 | -- 89 | -- @since 0.0.1.0 90 | instance Eq ShortHash where 91 | (ShortHash sh1) == (ShortHash sh2) = 92 | foreignPtrEq sh1 sh2 cryptoShortHashSipHashX24Bytes 93 | 94 | -- | 95 | -- 96 | -- @since 0.0.1.0 97 | instance Ord ShortHash where 98 | compare (ShortHash sh1) (ShortHash sh2) = 99 | foreignPtrOrd sh1 sh2 cryptoShortHashSipHashX24Bytes 100 | 101 | -- | 102 | -- 103 | -- @since 0.0.1.0 104 | instance Show ShortHash where 105 | show = Text.unpack . shortHashToHexText 106 | 107 | -- | 108 | -- 109 | -- @since 0.0.1.0 110 | instance Display ShortHash where 111 | displayBuilder = Builder.fromText . shortHashToHexText 112 | 113 | -- | Hash a 'StrictByteString'. 114 | -- 115 | -- The same message hashed with the same key will always produce the same output. 116 | -- 117 | -- The 'ShortHash' is of length 'cryptoShortHashSipHashX24Bytes' 118 | -- 119 | -- @since 0.0.1.0 120 | hashByteString 121 | :: ShortHashKey 122 | -- ^ Random key produced by 'newKey' 123 | -> StrictByteString 124 | -- ^ Data to hash 125 | -> ShortHash 126 | hashByteString (ShortHashKey keyFPtr) message = unsafeDupablePerformIO $ 127 | BS.unsafeUseAsCStringLen message $ \(cString, cStringLen) -> do 128 | shortHashFPtr <- Foreign.mallocForeignPtrBytes (fromIntegral cryptoShortHashSipHashX24Bytes) 129 | Foreign.withForeignPtr keyFPtr $ \keyPtr -> 130 | Foreign.withForeignPtr shortHashFPtr $ \shortHashPtr -> do 131 | result <- 132 | cryptoShortHashX24 133 | shortHashPtr 134 | (Foreign.castPtr cString) 135 | (fromIntegral @Int @CULLong cStringLen) 136 | keyPtr 137 | when (result /= 0) $ throw ShortHashingException 138 | pure $ ShortHash shortHashFPtr 139 | 140 | -- | Hash a strict 'Text'. 141 | -- 142 | -- The same message hashed with the same key will always produce the same output. 143 | -- 144 | -- The 'ShortHash' is of length 'cryptoShortHashSipHashX24Bytes' 145 | -- 146 | -- @since 0.0.1.0 147 | hashText 148 | :: ShortHashKey 149 | -- ^ Random key produced by 'newKey' 150 | -> Text 151 | -- ^ UTF-8 encoded data to hash 152 | -> ShortHash 153 | hashText key message = hashByteString key (Text.encodeUtf8 message) 154 | 155 | -- | Convert a 'ShortHash' to a strict binary 'StrictByteString'. 156 | -- 157 | -- @since 0.0.1.0 158 | shortHashToBinary :: ShortHash -> StrictByteString 159 | shortHashToBinary (ShortHash hashFPtr) = 160 | BS.fromForeignPtr 161 | (Foreign.castForeignPtr hashFPtr) 162 | 0 163 | (fromIntegral @CSize @Int cryptoShortHashSipHashX24Bytes) 164 | 165 | -- | Convert a 'ShortHash' to a hexadecimal-encoded 'StrictByteString' in constant time. 166 | -- 167 | -- @since 0.0.1.0 168 | shortHashToHexByteString :: ShortHash -> StrictByteString 169 | shortHashToHexByteString (ShortHash hashForeignPtr) = 170 | binaryToHex hashForeignPtr cryptoShortHashSipHashX24Bytes 171 | 172 | -- | Convert a 'ShortHash' to a strict hexadecimal-encoded 'Text'. 173 | -- 174 | -- @since 0.0.1.0 175 | shortHashToHexText :: ShortHash -> Text 176 | shortHashToHexText = Base16.extractBase16 . Base16.encodeBase16 . shortHashToBinary 177 | 178 | -- | A random key used for hashing, of size 'cryptoShortHashSipHashX24KeyBytes'. 179 | -- 180 | -- The same message hashed with the same key will always produce the same output. 181 | -- 182 | -- @since 0.0.1.0 183 | newtype ShortHashKey = ShortHashKey (ForeignPtr CUChar) 184 | 185 | -- | 186 | -- 187 | -- @since 0.0.1.0 188 | instance Eq ShortHashKey where 189 | (ShortHashKey sh1) == (ShortHashKey sh2) = 190 | foreignPtrEq sh1 sh2 cryptoShortHashSipHashX24Bytes 191 | 192 | -- | 193 | -- 194 | -- @since 0.0.1.0 195 | instance Ord ShortHashKey where 196 | compare (ShortHashKey sh1) (ShortHashKey sh2) = 197 | foreignPtrOrd sh1 sh2 cryptoShortHashSipHashX24Bytes 198 | 199 | -- | 200 | -- 201 | -- @since 0.0.1.0 202 | instance Show ShortHashKey where 203 | show = Text.unpack . shortHashKeyToHexText 204 | 205 | instance Display ShortHashKey where 206 | displayBuilder = Builder.fromText . shortHashKeyToHexText 207 | 208 | -- | Generate a random 'ShortHashKey' of size 'cryptoShortHashSipHashX24KeyBytes' 209 | -- 210 | -- @since 0.0.1.0 211 | newKey :: IO ShortHashKey 212 | newKey = do 213 | shortHashKeyForeignPtr <- 214 | Foreign.mallocForeignPtrBytes (fromIntegral cryptoShortHashSipHashX24KeyBytes) 215 | Foreign.withForeignPtr shortHashKeyForeignPtr $ \shortHashKeyPtr -> 216 | void $ cryptoShortHashX24KeyGen shortHashKeyPtr 217 | pure $ ShortHashKey shortHashKeyForeignPtr 218 | 219 | -- | Convert a 'ShortHash' to a strict binary 'StrictByteString'. 220 | -- 221 | -- @since 0.0.1.0 222 | shortHashKeyToBinary :: ShortHashKey -> StrictByteString 223 | shortHashKeyToBinary (ShortHashKey hashKeyFPtr) = 224 | BS.fromForeignPtr 225 | (Foreign.castForeignPtr hashKeyFPtr) 226 | 0 227 | (fromIntegral @CSize @Int cryptoShortHashSipHashX24KeyBytes) 228 | 229 | -- | Convert a 'ShortHashKey' to a hexadecimal-encoded 'StrictByteString' in constant time. 230 | -- 231 | -- @since 0.0.1.0 232 | shortHashKeyToHexByteString :: ShortHashKey -> StrictByteString 233 | shortHashKeyToHexByteString (ShortHashKey hashKeyForeignPtr) = 234 | binaryToHex hashKeyForeignPtr cryptoShortHashSipHashX24KeyBytes 235 | 236 | -- | Convert a 'ShortHash' to a strict hexadecimal-encoded 'Text'. 237 | -- 238 | -- @since 0.0.1.0 239 | shortHashKeyToHexText :: ShortHashKey -> Text 240 | shortHashKeyToHexText = Base16.extractBase16 . Base16.encodeBase16 . shortHashKeyToBinary 241 | 242 | -- | Convert a binary 'StrictByteString' to a 'ShortHashKey'. 243 | -- 244 | -- The input key must be of length 'cryptoShortHashSipHashX24KeyBytes' 245 | -- 246 | -- @since 0.0.1.0 247 | binaryToShortHashKey :: StrictByteString -> Maybe ShortHashKey 248 | binaryToShortHashKey binaryKey = 249 | if BS.length binaryKey /= fromIntegral cryptoShortHashSipHashX24KeyBytes 250 | then Nothing 251 | else unsafeDupablePerformIO $ do 252 | BS.unsafeUseAsCString binaryKey $ \cString -> do 253 | shortHashKeyFPtr <- Foreign.mallocForeignPtrBytes (fromIntegral cryptoShortHashSipHashX24KeyBytes) 254 | Foreign.withForeignPtr shortHashKeyFPtr $ \shortHashKeyPtr -> 255 | Foreign.copyBytes 256 | shortHashKeyPtr 257 | (Foreign.castPtr @CChar @CUChar cString) 258 | (fromIntegral cryptoShortHashSipHashX24KeyBytes) 259 | pure $ Just $ ShortHashKey shortHashKeyFPtr 260 | 261 | -- | Convert a strict hexadecimal-encoded 'Text' to a 'ShortHashKey'. 262 | -- 263 | -- The input key, once decoded from base16, must be of length 'cryptoShortHashSipHashX24KeyBytes' 264 | -- 265 | -- @since 0.0.1.0 266 | hexTextToShortHashKey :: Text -> Maybe ShortHashKey 267 | hexTextToShortHashKey = hexByteStringToShortHashKey . Text.encodeUtf8 268 | 269 | -- | Convert a hexadecimal-encoded 'StrictByteString' to a 'ShortHashKey'. 270 | -- 271 | -- The input key, once decoded from base16, must be of length 'cryptoShortHashSipHashX24KeyBytes' 272 | -- 273 | -- @since 0.0.1.0 274 | hexByteStringToShortHashKey :: StrictByteString -> Maybe ShortHashKey 275 | hexByteStringToShortHashKey hexByteString = 276 | case Base16.decodeBase16Untyped hexByteString of 277 | Right binary -> binaryToShortHashKey binary 278 | Left _ -> Nothing 279 | 280 | -- | Exception thrown upon error during hashing by 281 | -- 'hashByteString' or 'hashText'. 282 | -- 283 | -- @since 0.0.1.0 284 | data ShortHashingException = ShortHashingException 285 | deriving stock 286 | ( Eq 287 | -- ^ @since 0.0.1.0 288 | , Ord 289 | -- ^ @since 0.0.1.0 290 | , Show 291 | -- ^ @since 0.0.1.0 292 | ) 293 | deriving anyclass 294 | ( Exception 295 | -- ^ @since 0.0.1.0 296 | ) 297 | deriving 298 | ( Display 299 | -- ^ @since 0.0.1.0 300 | ) 301 | via (ShowInstance ShortHashingException) 302 | -------------------------------------------------------------------------------- /sel/src/Sel/Internal.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE BangPatterns #-} 2 | {-# LANGUAGE ImportQualifiedPost #-} 3 | {-# LANGUAGE KindSignatures #-} 4 | {-# LANGUAGE ScopedTypeVariables #-} 5 | {-# LANGUAGE TypeApplications #-} 6 | {-# LANGUAGE ViewPatterns #-} 7 | 8 | module Sel.Internal where 9 | 10 | import Control.Monad (when) 11 | import Control.Monad.IO.Class (MonadIO, liftIO) 12 | import Control.Monad.Trans.Class (lift) 13 | import Data.Base16.Types qualified as Base16 14 | import Data.ByteString (StrictByteString) 15 | import Data.ByteString.Base16 qualified as Base16 16 | import Data.ByteString.Internal (memcmp) 17 | import Data.ByteString.Internal qualified as ByteString 18 | import Data.Coerce (coerce) 19 | import Data.Kind (Type) 20 | import Foreign (ForeignPtr, Ptr) 21 | import Foreign qualified 22 | import Foreign.C (CSize, CUChar, throwErrno) 23 | import Foreign.C.Types (CChar) 24 | import LibSodium.Bindings.Comparison (sodiumCompare, sodiumMemcmp) 25 | import LibSodium.Bindings.SecureMemory (finalizerSodiumFree, sodiumFree, sodiumMalloc) 26 | import System.IO.Unsafe (unsafeDupablePerformIO) 27 | 28 | import Sel.Internal.Scoped 29 | import Sel.Internal.Scoped.Foreign 30 | 31 | -- | Compare the contents of two byte arrays for equality in constant time. 32 | -- 33 | -- /See:/ [Constant-time test for equality](https://doc.libsodium.org/helpers#constant-time-test-for-equality) 34 | -- 35 | -- @since 0.0.3.0 36 | foreignPtrEqConstantTime :: ForeignPtr CUChar -> ForeignPtr CUChar -> CSize -> Bool 37 | foreignPtrEqConstantTime p q size = 38 | unsafeDupablePerformIO . fmap (== 0) . use $ 39 | sodiumMemcmp <$> foreignPtr p <*> foreignPtr q <*> pure size 40 | 41 | -- | Lexicographically compare the contents of two byte arrays. 42 | -- 43 | -- ⚠️ Such comparisons are vulnerable to timing attacks, and should be 44 | -- avoided for secret data. 45 | -- 46 | -- @since 0.0.1.0 47 | foreignPtrOrd :: ForeignPtr CUChar -> ForeignPtr CUChar -> CSize -> Ordering 48 | foreignPtrOrd p q size = 49 | unsafeDupablePerformIO . fmap (`compare` 0) . useM $ 50 | memcmp 51 | <$> foreignPtr (coerce p) 52 | <*> foreignPtr (coerce q) 53 | <*> pure (fromIntegral size) 54 | 55 | -- | Lexicographically compare the contents of two byte arrays in constant time. 56 | -- 57 | -- /See:/ [Comparing large numbers](https://libsodium.gitbook.io/doc/helpers#comparing-large-numbers) 58 | -- 59 | -- @since 0.0.3.0 60 | foreignPtrOrdConstantTime :: ForeignPtr CUChar -> ForeignPtr CUChar -> CSize -> Ordering 61 | foreignPtrOrdConstantTime p q size = 62 | unsafeDupablePerformIO . fmap (`compare` 0) . useM $ 63 | sodiumCompare <$> foreignPtr p <*> foreignPtr q <*> pure size 64 | 65 | -- | Compare two byte arrays for lexicographic equality. 66 | -- 67 | -- ⚠️ Such comparisons are vulnerable to timing attacks, and should be 68 | -- avoided for secret data. 69 | -- 70 | -- @since 0.0.1.0 71 | foreignPtrEq :: ForeignPtr CUChar -> ForeignPtr CUChar -> CSize -> Bool 72 | foreignPtrEq p q size = foreignPtrOrd p q size == EQ 73 | 74 | -- | Convert a @'ForeignPtr' a@ to a 'ByteString' of the given length 75 | -- and render the hexadecimal-encoded bytes as a 'String'. 76 | -- 77 | -- @since 0.0.1.0 78 | foreignPtrShow :: ForeignPtr a -> CSize -> String 79 | foreignPtrShow (Foreign.castForeignPtr -> cstring) size = 80 | ByteString.unpackChars . Base16.extractBase16 . Base16.encodeBase16' $ 81 | ByteString.fromForeignPtr cstring 0 (fromIntegral @CSize @Int size) 82 | 83 | -- | Copy a byte array to a @libsodium@ pointer. 84 | -- 85 | -- The size of the array is not checked. The input may be truncated if 86 | -- it is too long, or an unchecked exception may be thrown if it is 87 | -- too short. 88 | -- 89 | -- @since 0.0.3.0 90 | unsafeCopyToSodiumPointer :: CSize -> StrictByteString -> IO (ForeignPtr CUChar) 91 | unsafeCopyToSodiumPointer size s = use $ do 92 | str <- unsafeCString s 93 | lift $ sodiumPointer size $ \k -> 94 | Foreign.copyArray 95 | (Foreign.castPtr @CUChar @CChar k) 96 | str 97 | (fromIntegral @CSize @Int size) 98 | 99 | -- | Allocate secure memory and populate it with the provided action. 100 | -- 101 | -- Memory is allocated with 'LibSodium.Bindings.SecureMemory.sodiumMalloc' (see notes). 102 | -- 103 | -- A finalizer frees the memory when the key goes out of scope. 104 | -- 105 | -- @since 0.0.3.0 106 | sodiumPointer :: CSize -> (Ptr CUChar -> IO ()) -> IO (ForeignPtr CUChar) 107 | sodiumPointer size action = do 108 | ptr <- sodiumMalloc size 109 | when (ptr == Foreign.nullPtr) $ do 110 | throwErrno "sodium_malloc" 111 | action ptr 112 | Foreign.newForeignPtr finalizerSodiumFree ptr 113 | 114 | -- | Securely allocate an amount of memory with 'sodiumMalloc' and pass 115 | -- a pointer to the region to the provided action. 116 | -- The region is deallocated with 'sodiumFree' afterwards. 117 | -- Do not try to jailbreak the pointer outside of the action, 118 | -- this will not be pleasant. 119 | allocateWith 120 | :: forall (a :: Type) (b :: Type) (m :: Type -> Type) 121 | . MonadIO m 122 | => CSize 123 | -- ^ Amount of memory to allocate 124 | -> (Ptr a -> m b) 125 | -- ^ Action to perform on the memory 126 | -> m b 127 | allocateWith size action = do 128 | !ptr <- liftIO $ sodiumMalloc size 129 | !result <- action ptr 130 | liftIO $ sodiumFree ptr 131 | pure result 132 | -------------------------------------------------------------------------------- /sel/src/Sel/Internal/Scoped.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE InstanceSigs #-} 2 | {-# LANGUAGE PolyKinds #-} 3 | {-# LANGUAGE RankNTypes #-} 4 | {-# LANGUAGE StandaloneKindSignatures #-} 5 | {-# LANGUAGE TypeFamilies #-} 6 | {-# LANGUAGE TypeOperators #-} 7 | {-# LANGUAGE UndecidableInstances #-} 8 | 9 | -- | 10 | -- Module : Sel.Internal.Scoped 11 | -- Description : Continuation-passing utilities 12 | -- Copyright : (c) Jack Henahan, 2024 13 | -- License : BSD-3-Clause 14 | -- Maintainer : The Haskell Cryptography Group 15 | -- Portability : GHC only 16 | -- 17 | -- This module implements a version of @Codensity@, modeling delimited 18 | -- continuations. Useful for avoiding extreme rightward drift in 19 | -- chains of @withForeignPtr@ and friends. 20 | module Sel.Internal.Scoped where 21 | 22 | import Control.Monad (ap, void) 23 | import Control.Monad.IO.Class (MonadIO (liftIO)) 24 | import Control.Monad.Trans.Class (MonadTrans (lift)) 25 | import Data.Kind (Type) 26 | import Data.Type.Equality (type (~~)) 27 | import GHC.Exts (RuntimeRep, TYPE) 28 | 29 | -- | @since 0.0.3.0 30 | type Scoped :: forall {k} {rep :: RuntimeRep}. (k -> TYPE rep) -> Type -> Type 31 | newtype Scoped m a = Scoped {runScoped :: forall b. (a -> m b) -> m b} 32 | 33 | -- | @since 0.0.3.0 34 | instance Functor (Scoped f) where 35 | fmap f (Scoped m) = Scoped $ \k -> m (k . f) 36 | {-# INLINE fmap #-} 37 | 38 | -- | @since 0.0.3.0 39 | instance Applicative (Scoped f) where 40 | pure a = Scoped $ \k -> k a 41 | {-# INLINE pure #-} 42 | 43 | (<*>) = ap 44 | {-# INLINE (<*>) #-} 45 | 46 | -- | @since 0.0.3.0 47 | instance Monad (Scoped f) where 48 | Scoped m >>= f = Scoped $ \k -> 49 | m $ \a -> runScoped (f a) k 50 | {-# INLINE (>>=) #-} 51 | 52 | -- | @since 0.0.3.0 53 | instance (MonadIO m', m' ~~ m) => MonadIO (Scoped m) where 54 | liftIO = lift . liftIO 55 | {-# INLINE liftIO #-} 56 | 57 | -- | @since 0.0.3.0 58 | instance MonadTrans Scoped where 59 | lift m = Scoped (m >>=) 60 | {-# INLINE lift #-} 61 | 62 | -- | @since 0.0.3.0 63 | reset :: Monad m => Scoped m a -> Scoped m a 64 | reset = lift . use 65 | 66 | -- | @since 0.0.3.0 67 | shift :: Applicative m => (forall b. (a -> m b) -> Scoped m b) -> Scoped m a 68 | shift f = Scoped $ use . f 69 | 70 | -- | @since 0.0.3.0 71 | use :: Applicative m => Scoped m a -> m a 72 | use (Scoped m) = m pure 73 | 74 | -- | @since 0.0.3.0 75 | useM :: Monad m => Scoped m (m a) -> m a 76 | useM f = use $ f >>= lift 77 | 78 | -- | @since 0.0.3.0 79 | use_ :: Applicative m => Scoped m a -> m () 80 | use_ = void . use 81 | 82 | -- | @since 0.0.3.0 83 | useM_ :: Monad m => Scoped m (m a) -> m () 84 | useM_ = void . useM 85 | -------------------------------------------------------------------------------- /sel/src/Sel/Internal/Scoped/Foreign.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ImportQualifiedPost #-} 2 | 3 | -- | 4 | -- Module : Sel.Internal.Scoped.Foreign 5 | -- Description : Scoped wrappers around pointer manipulation 6 | -- Copyright : (c) Jack Henahan, 2024 7 | -- License : BSD-3-Clause 8 | -- Maintainer : The Haskell Cryptography Group 9 | -- Portability : GHC only 10 | -- 11 | -- This module wraps some common points of contact with 'Ptr', 12 | -- 'ForeignPtr', and friends up in 'Scoped' for the sake of not saying 13 | -- 'lift' absolutely everywhere. 14 | module Sel.Internal.Scoped.Foreign where 15 | 16 | import Control.Monad.Trans.Class (lift) 17 | import Data.ByteString (StrictByteString) 18 | import Data.ByteString.Unsafe qualified as ByteString 19 | import Foreign (ForeignPtr, Ptr, Storable) 20 | import Foreign qualified 21 | import Foreign.C (CString, CStringLen) 22 | 23 | import Sel.Internal.Scoped 24 | 25 | -- | @since 0.0.3.0 26 | foreignPtr :: ForeignPtr a -> Scoped IO (Ptr a) 27 | foreignPtr fptr = Scoped $ Foreign.withForeignPtr fptr 28 | 29 | -- | @since 0.0.3.0 30 | unsafeCStringLen :: StrictByteString -> Scoped IO CStringLen 31 | unsafeCStringLen bs = Scoped $ ByteString.unsafeUseAsCStringLen bs 32 | 33 | -- | @since 0.0.3.0 34 | unsafeCString :: StrictByteString -> Scoped IO CString 35 | unsafeCString bs = Scoped $ ByteString.unsafeUseAsCString bs 36 | 37 | -- | @since 0.0.3.0 38 | mallocBytes :: Int -> Scoped IO (Ptr a) 39 | mallocBytes = lift . Foreign.mallocBytes 40 | 41 | -- | @since 0.0.3.0 42 | mallocForeignPtrBytes :: Int -> Scoped IO (ForeignPtr a) 43 | mallocForeignPtrBytes len = lift $ Foreign.mallocForeignPtrBytes len 44 | 45 | -- | @since 0.0.3.0 46 | copyArray :: Storable a => Ptr a -> Ptr a -> Int -> Scoped IO () 47 | copyArray target source len = lift $ Foreign.copyArray target source len 48 | -------------------------------------------------------------------------------- /sel/src/Sel/Internal/Sodium.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ImportQualifiedPost #-} 2 | 3 | module Sel.Internal.Sodium where 4 | 5 | import Control.Monad.Trans.Class (lift) 6 | import Data.ByteString (StrictByteString) 7 | import Data.ByteString qualified as ByteString 8 | import Foreign (ForeignPtr) 9 | import Foreign.C (CSize, CUChar) 10 | import LibSodium.Bindings.Utils 11 | import System.IO.Unsafe (unsafeDupablePerformIO) 12 | 13 | import Sel.Internal.Scoped 14 | import Sel.Internal.Scoped.Foreign 15 | 16 | -- | Convert a byte array to a hexadecimal-encoded 'StrictByteString' in constant time. 17 | -- 18 | -- /See:/ [@sodium_bin2hex@](https://libsodium.gitbook.io/doc/helpers#hexadecimal-encoding-decoding) 19 | -- 20 | -- @since 0.0.3.0 21 | binaryToHex :: ForeignPtr CUChar -> CSize -> StrictByteString 22 | binaryToHex fPtr size = unsafeDupablePerformIO . use $ do 23 | let hexLength = size * 2 + 1 24 | hexPtr <- foreignPtr =<< mallocForeignPtrBytes (fromIntegral hexLength) 25 | ptr <- foreignPtr fPtr 26 | lift $ ByteString.packCString =<< sodiumBin2Hex hexPtr hexLength ptr size 27 | -------------------------------------------------------------------------------- /sel/src/Sel/PublicKey/Seal.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE DerivingVia #-} 3 | {-# LANGUAGE ScopedTypeVariables #-} 4 | {-# LANGUAGE TypeApplications #-} 5 | 6 | -- | 7 | -- 8 | -- Module: Sel.PublicKey.Seal 9 | -- Description: Anonymous ephemeral authenticated encryption with public and secret keys 10 | -- Copyright: (C) Hécate Moonlight 2022 11 | -- License: BSD-3-Clause 12 | -- Maintainer: The Haskell Cryptography Group 13 | -- Portability: GHC only 14 | module Sel.PublicKey.Seal 15 | ( -- ** Introduction 16 | -- $introduction 17 | 18 | -- ** Usage 19 | -- $usage 20 | 21 | -- ** Keys 22 | PublicKey (..) 23 | , SecretKey (..) 24 | , newKeyPair 25 | 26 | -- ** Operations 27 | , seal 28 | , open 29 | 30 | -- ** Errors 31 | , KeyPairGenerationException 32 | , EncryptionError 33 | ) where 34 | 35 | import Control.Exception (throw) 36 | import Control.Monad (when) 37 | import Data.ByteString (StrictByteString) 38 | import qualified Data.ByteString.Unsafe as BS 39 | import qualified Foreign 40 | import Foreign.C (CChar, CSize, CUChar, CULLong) 41 | import LibSodium.Bindings.SealedBoxes 42 | ( cryptoBoxSeal 43 | , cryptoBoxSealOpen 44 | , cryptoBoxSealbytes 45 | ) 46 | import System.IO.Unsafe (unsafeDupablePerformIO) 47 | 48 | import Sel.PublicKey.Cipher 49 | ( Ciphertext (Ciphertext) 50 | , EncryptionError (..) 51 | , KeyPairGenerationException 52 | , PublicKey (PublicKey) 53 | , SecretKey (..) 54 | , newKeyPair 55 | ) 56 | 57 | -- $introduction 58 | -- Ephemeral authenticated encryption allows to anonymously send message to 59 | -- a recipient given their public key. 60 | -- 61 | -- Only the recipient can decrypt these messages using their own secret key. 62 | -- While the recipient can verify the integrity of the message, they cannot 63 | -- verify the identity of the sender. 64 | -- 65 | -- A message is encrypted using an ephemeral key pair, with the secret key being erased 66 | -- right after the encryption process. 67 | -- 68 | -- Without knowing the secret key used for a given message, the sender cannot decrypt 69 | -- their own message later. Furthermore, without additional data, a message cannot 70 | -- be correlated with the identity of its sender. 71 | 72 | -- $usage 73 | -- 74 | -- > import qualified Sel.PublicKey.Seal as Seal 75 | -- > import Sel (secureMain) 76 | -- > 77 | -- > main = secureMain $ do 78 | -- > -- We get the recipient their pair of keys: 79 | -- > (recipientPublicKey, recipientSecretKey) <- newKeyPair 80 | -- > encryptedMessage <- Seal.encrypt "hello hello" recipientPublicKey 81 | -- > let result = Seal.open encryptedMessage recipientPublicKey recipientSecretKey 82 | -- > print result 83 | -- > -- "Just \"hello hello\"" 84 | 85 | -- | Encrypt a message with the recipient's public key. A key pair for the sender 86 | -- is generated, and the public key of that pair is attached to the cipher text. 87 | -- The secret key of the sender's pair is automatically destroyed. 88 | -- 89 | -- @since 0.0.1.0 90 | seal 91 | :: StrictByteString 92 | -- ^ Message to encrypt 93 | -> PublicKey 94 | -- ^ Public key of the recipient 95 | -> IO Ciphertext 96 | seal messageByteString (PublicKey publicKeyFptr) = do 97 | BS.unsafeUseAsCStringLen messageByteString $ \(messagePtr, messageLen) -> do 98 | ciphertextForeignPtr <- 99 | Foreign.mallocForeignPtrBytes 100 | (messageLen + fromIntegral cryptoBoxSealbytes) 101 | Foreign.withForeignPtr publicKeyFptr $ \publicKeyPtr -> 102 | Foreign.withForeignPtr ciphertextForeignPtr $ \ciphertextPtr -> do 103 | result <- 104 | cryptoBoxSeal 105 | ciphertextPtr 106 | (Foreign.castPtr @CChar @CUChar messagePtr) 107 | (fromIntegral @Int @CULLong messageLen) 108 | publicKeyPtr 109 | when (result /= 0) $ throw EncryptionError 110 | pure $ 111 | Ciphertext 112 | (fromIntegral @Int @CULLong messageLen) 113 | ciphertextForeignPtr 114 | 115 | -- | Open a sealed message from an unknown sender. 116 | -- You need your public and secret keys. 117 | -- 118 | -- @since 0.0.1.0 119 | open 120 | :: Ciphertext 121 | -- ^ Cipher to decrypt 122 | -> PublicKey 123 | -- ^ Public key of the recipient 124 | -> SecretKey 125 | -- ^ Secret key of the recipient 126 | -> Maybe StrictByteString 127 | open 128 | (Ciphertext messageLen cipherForeignPtr) 129 | (PublicKey publicKeyFPtr) 130 | (SecretKey secretKeyFPtr) = unsafeDupablePerformIO $ do 131 | messagePtr <- Foreign.mallocBytes (fromIntegral @CULLong @Int messageLen) 132 | Foreign.withForeignPtr cipherForeignPtr $ \ciphertextPtr -> 133 | Foreign.withForeignPtr publicKeyFPtr $ \publicKeyPtr -> 134 | Foreign.withForeignPtr secretKeyFPtr $ \secretKeyPtr -> do 135 | result <- 136 | cryptoBoxSealOpen 137 | messagePtr 138 | ciphertextPtr 139 | (messageLen + fromIntegral @CSize @CULLong cryptoBoxSealbytes) 140 | publicKeyPtr 141 | secretKeyPtr 142 | case result of 143 | (-1) -> pure Nothing 144 | _ -> do 145 | bsPtr <- Foreign.mallocBytes (fromIntegral messageLen) 146 | Foreign.copyBytes bsPtr messagePtr (fromIntegral messageLen) 147 | Just 148 | <$> BS.unsafePackMallocCStringLen 149 | (Foreign.castPtr @CUChar @CChar bsPtr, fromIntegral messageLen) 150 | -------------------------------------------------------------------------------- /sel/src/Sel/PublicKey/Signature.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DerivingStrategies #-} 2 | {-# LANGUAGE NamedFieldPuns #-} 3 | {-# LANGUAGE ScopedTypeVariables #-} 4 | {-# LANGUAGE TypeApplications #-} 5 | 6 | -- | 7 | -- 8 | -- Module: Sel.PublicKey.Signature 9 | -- Description: Public-key signatures with the Ed25519 algorithm 10 | -- Copyright: (C) Hécate Moonlight 2022 11 | -- License: BSD-3-Clause 12 | -- Maintainer: The Haskell Cryptography Group 13 | -- Portability: GHC only 14 | module Sel.PublicKey.Signature 15 | ( -- ** Introduction 16 | -- $introduction 17 | PublicKey 18 | , SecretKey 19 | , SignedMessage 20 | 21 | -- ** Key Pair generation 22 | , generateKeyPair 23 | 24 | -- ** Message Signing 25 | , signMessage 26 | , openMessage 27 | 28 | -- ** Constructing and Deconstructing 29 | , getSignature 30 | , unsafeGetMessage 31 | , mkSignature 32 | ) 33 | where 34 | 35 | import Control.Monad (void) 36 | import Data.ByteString (StrictByteString) 37 | import Data.ByteString.Unsafe (unsafePackMallocCStringLen) 38 | import qualified Data.ByteString.Unsafe as ByteString 39 | import Foreign 40 | ( ForeignPtr 41 | , Ptr 42 | , castPtr 43 | , mallocBytes 44 | , mallocForeignPtrBytes 45 | , withForeignPtr 46 | ) 47 | import Foreign.C (CChar, CSize, CUChar, CULLong) 48 | import qualified Foreign.Marshal.Array as Foreign 49 | import qualified Foreign.Ptr as Foreign 50 | import GHC.IO.Handle.Text (memcpy) 51 | import LibSodium.Bindings.CryptoSign 52 | ( cryptoSignBytes 53 | , cryptoSignDetached 54 | , cryptoSignKeyPair 55 | , cryptoSignPublicKeyBytes 56 | , cryptoSignSecretKeyBytes 57 | , cryptoSignVerifyDetached 58 | ) 59 | import System.IO.Unsafe (unsafeDupablePerformIO) 60 | 61 | import Sel.Internal 62 | 63 | -- $introduction 64 | -- 65 | -- Public-key Signatures work with a 'SecretKey' and 'PublicKey' 66 | -- 67 | -- * The 'SecretKey' is used to append a signature to any number of messages. It must stay private; 68 | -- * The 'PublicKey' is used by third-parties to to verify that the signature appended to a message was 69 | -- issued by the creator of the public key. It must be distributed to third-parties. 70 | -- 71 | -- Verifiers need to already know and ultimately trust a public key before messages signed 72 | -- using it can be verified. 73 | 74 | -- | 75 | -- 76 | -- @since 0.0.1.0 77 | newtype PublicKey = PublicKey (ForeignPtr CUChar) 78 | 79 | -- | 80 | -- 81 | -- @since 0.0.1.0 82 | instance Eq PublicKey where 83 | (PublicKey pk1) == (PublicKey pk2) = 84 | foreignPtrEq pk1 pk2 cryptoSignPublicKeyBytes 85 | 86 | -- | 87 | -- 88 | -- @since 0.0.1.0 89 | instance Ord PublicKey where 90 | compare (PublicKey pk1) (PublicKey pk2) = 91 | foreignPtrOrd pk1 pk2 cryptoSignPublicKeyBytes 92 | 93 | -- | 94 | -- 95 | -- @since 0.0.1.0 96 | newtype SecretKey = SecretKey (ForeignPtr CUChar) 97 | 98 | -- | 99 | -- 100 | -- @since 0.0.1.0 101 | instance Eq SecretKey where 102 | (SecretKey sk1) == (SecretKey sk2) = 103 | foreignPtrEqConstantTime sk1 sk2 cryptoSignSecretKeyBytes 104 | 105 | -- | 106 | -- 107 | -- @since 0.0.1.0 108 | instance Ord SecretKey where 109 | compare (SecretKey sk1) (SecretKey sk2) = 110 | foreignPtrOrd sk1 sk2 cryptoSignSecretKeyBytes 111 | 112 | -- | 113 | -- 114 | -- @since 0.0.1.0 115 | data SignedMessage = SignedMessage 116 | { messageLength :: CSize 117 | , messageForeignPtr :: ForeignPtr CUChar 118 | , signatureForeignPtr :: ForeignPtr CUChar 119 | } 120 | 121 | -- | 122 | -- 123 | -- @since 0.0.1.0 124 | instance Eq SignedMessage where 125 | (SignedMessage len1 msg1 sig1) == (SignedMessage len2 msg2 sig2) = 126 | let 127 | messageLength = len1 == len2 128 | msg1Eq = foreignPtrEq msg1 msg2 len1 129 | msg2Eq = foreignPtrEq sig1 sig2 cryptoSignBytes 130 | in 131 | messageLength && msg1Eq && msg2Eq 132 | 133 | -- | 134 | -- 135 | -- @since 0.0.1.0 136 | instance Ord SignedMessage where 137 | compare (SignedMessage len1 msg1 sig1) (SignedMessage len2 msg2 sig2) = 138 | let 139 | messageLength = compare len1 len2 140 | msg1Ord = foreignPtrOrd msg1 msg2 len1 141 | msg2Ord = foreignPtrOrd sig1 sig2 cryptoSignBytes 142 | in 143 | messageLength <> msg1Ord <> msg2Ord 144 | 145 | -- | Generate a pair of public and secret key. 146 | -- 147 | -- The length parameters used are 'cryptoSignPublicKeyBytes' 148 | -- and 'cryptoSignSecretKeyBytes'. 149 | -- 150 | -- @since 0.0.1.0 151 | generateKeyPair :: IO (PublicKey, SecretKey) 152 | generateKeyPair = do 153 | publicKeyForeignPtr <- mallocForeignPtrBytes (fromIntegral @CSize @Int cryptoSignPublicKeyBytes) 154 | secretKeyForeignPtr <- mallocForeignPtrBytes (fromIntegral @CSize @Int cryptoSignSecretKeyBytes) 155 | withForeignPtr publicKeyForeignPtr $ \pkPtr -> 156 | withForeignPtr secretKeyForeignPtr $ \skPtr -> 157 | void $ 158 | cryptoSignKeyPair 159 | pkPtr 160 | skPtr 161 | pure (PublicKey publicKeyForeignPtr, SecretKey secretKeyForeignPtr) 162 | 163 | -- | Sign a message. 164 | -- 165 | -- Note that, if @libsodium@ is compiled with the @ED25519_NONDETERMINISTIC@ 166 | -- macro defined, this function will produce non-deterministic (but also 167 | -- non-standard) Ed25519 signatures. If @libsodium@ hasn't been compiled with 168 | -- the @ED25519_NONDETERMINISTIC@ macro defined, it's safe to call this 169 | -- function in a pure context with 'unsafeDupablePerformIO'. 170 | -- 171 | -- For more information, see the 172 | -- [@libsodium@ docs](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#notes). 173 | -- 174 | -- @since 0.0.1.0 175 | signMessage :: StrictByteString -> SecretKey -> IO SignedMessage 176 | signMessage message (SecretKey skFPtr) = 177 | ByteString.unsafeUseAsCStringLen message $ \(cString, messageLength) -> do 178 | let sigLength = fromIntegral @CSize @Int cryptoSignBytes 179 | (messageForeignPtr :: ForeignPtr CUChar) <- Foreign.mallocForeignPtrBytes messageLength 180 | signatureForeignPtr <- Foreign.mallocForeignPtrBytes sigLength 181 | withForeignPtr messageForeignPtr $ \messagePtr -> 182 | withForeignPtr signatureForeignPtr $ \signaturePtr -> 183 | withForeignPtr skFPtr $ \skPtr -> do 184 | Foreign.copyArray messagePtr (Foreign.castPtr @CChar @CUChar cString) messageLength 185 | void $ 186 | cryptoSignDetached 187 | signaturePtr 188 | Foreign.nullPtr -- Always of size 'cryptoSignBytes' 189 | (castPtr @CChar @CUChar cString) 190 | (fromIntegral @Int @CULLong messageLength) 191 | skPtr 192 | pure $ SignedMessage (fromIntegral @Int @CSize messageLength) messageForeignPtr signatureForeignPtr 193 | 194 | -- | Open a signed message with the signatory's public key. 195 | -- The function returns 'Nothing' if there is a key mismatch. 196 | -- 197 | -- @since 0.0.1.0 198 | openMessage :: SignedMessage -> PublicKey -> Maybe StrictByteString 199 | openMessage SignedMessage{messageLength, messageForeignPtr, signatureForeignPtr} (PublicKey pkForeignPtr) = unsafeDupablePerformIO $ 200 | withForeignPtr pkForeignPtr $ \publicKeyPtr -> 201 | withForeignPtr signatureForeignPtr $ \signaturePtr -> do 202 | withForeignPtr messageForeignPtr $ \messagePtr -> do 203 | result <- 204 | cryptoSignVerifyDetached 205 | signaturePtr 206 | messagePtr 207 | (fromIntegral @CSize @CULLong messageLength) 208 | publicKeyPtr 209 | case result of 210 | (-1) -> pure Nothing 211 | _ -> do 212 | bsPtr <- mallocBytes (fromIntegral messageLength) 213 | memcpy bsPtr (castPtr messagePtr) messageLength 214 | Just <$> unsafePackMallocCStringLen (castPtr bsPtr :: Ptr CChar, fromIntegral messageLength) 215 | 216 | -- | Get the signature part of a 'SignedMessage'. 217 | -- 218 | -- @since 0.0.1.0 219 | getSignature :: SignedMessage -> StrictByteString 220 | getSignature SignedMessage{signatureForeignPtr} = unsafeDupablePerformIO $ 221 | withForeignPtr signatureForeignPtr $ \signaturePtr -> do 222 | bsPtr <- Foreign.mallocBytes (fromIntegral cryptoSignBytes) 223 | memcpy bsPtr signaturePtr cryptoSignBytes 224 | unsafePackMallocCStringLen (Foreign.castPtr bsPtr :: Ptr CChar, fromIntegral cryptoSignBytes) 225 | 226 | -- | Get the message part of a 'SignedMessage' __without verifying the signature__. 227 | -- 228 | -- @since 0.0.1.0 229 | unsafeGetMessage :: SignedMessage -> StrictByteString 230 | unsafeGetMessage SignedMessage{messageLength, messageForeignPtr} = unsafeDupablePerformIO $ 231 | withForeignPtr messageForeignPtr $ \messagePtr -> do 232 | bsPtr <- Foreign.mallocBytes (fromIntegral messageLength) 233 | memcpy bsPtr messagePtr messageLength 234 | unsafePackMallocCStringLen (Foreign.castPtr bsPtr :: Ptr CChar, fromIntegral messageLength) 235 | 236 | -- | Combine a message and a signature into a 'SignedMessage'. 237 | -- 238 | -- @since 0.0.1.0 239 | mkSignature :: StrictByteString -> StrictByteString -> SignedMessage 240 | mkSignature message signature = unsafeDupablePerformIO $ 241 | ByteString.unsafeUseAsCStringLen message $ \(messageStringPtr, messageLength) -> 242 | ByteString.unsafeUseAsCStringLen signature $ \(signatureStringPtr, _) -> do 243 | (messageForeignPtr :: ForeignPtr CUChar) <- Foreign.mallocForeignPtrBytes messageLength 244 | signatureForeignPtr <- Foreign.mallocForeignPtrBytes (fromIntegral cryptoSignBytes) 245 | withForeignPtr messageForeignPtr $ \messagePtr -> 246 | withForeignPtr signatureForeignPtr $ \signaturePtr -> do 247 | Foreign.copyArray messagePtr (Foreign.castPtr messageStringPtr) messageLength 248 | Foreign.copyArray signaturePtr (Foreign.castPtr signatureStringPtr) (fromIntegral cryptoSignBytes) 249 | pure $ SignedMessage (fromIntegral @Int @CSize messageLength) messageForeignPtr signatureForeignPtr 250 | -------------------------------------------------------------------------------- /sel/src/Sel/Scrypt.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TypeApplications #-} 2 | 3 | -- | 4 | -- 5 | -- Module: Sel.Scrypt 6 | -- Description: Hashing with the Scrypt algorithm. 7 | -- Copyright: (C) Seth Paul Hubbard 2023 8 | -- License: BSD-3-Clause 9 | -- Maintainer: The Haskell Cryptography Group 10 | -- Stability: Stable 11 | -- Portability: GHC only 12 | module Sel.Scrypt 13 | ( -- ** Introduction 14 | -- $introduction 15 | ScryptHash 16 | 17 | -- ** Password Hashing and Verifying. 18 | , scryptHashPassword 19 | , scryptVerifyPassword 20 | 21 | -- *** Conversion 22 | , scryptHashToByteString 23 | , scryptHashToText 24 | , asciiTextToScryptHash 25 | , asciiByteStringToScryptHash 26 | ) 27 | where 28 | 29 | import Control.Monad (void) 30 | import Data.ByteString (StrictByteString) 31 | import qualified Data.ByteString.Internal as BS 32 | import Data.ByteString.Unsafe (unsafeUseAsCStringLen) 33 | import Data.Text as Text 34 | import qualified Data.Text.Builder.Linear as Builder 35 | import Data.Text.Display 36 | import qualified Data.Text.Encoding as Text 37 | import Foreign hiding (void) 38 | import Foreign.C 39 | import LibSodium.Bindings.Scrypt 40 | 41 | import Sel.Internal 42 | import Sel.Internal.Sodium (binaryToHex) 43 | 44 | -- $introduction 45 | -- 46 | -- This API is used for hashing and verifying passwords using the Scrypt algorithm. 47 | -- This module is provided for interoperability with other applications. If you do 48 | -- not need to use Scrypt specifically, use "Sel.Hashing.Password". 49 | 50 | -- | A hashed password from the Scrypt algorithm. 51 | -- 52 | -- @since 0.0.1.0 53 | newtype ScryptHash = ScryptHash (ForeignPtr CChar) 54 | 55 | -- | @since 0.0.1.0 56 | instance Eq ScryptHash where 57 | (ScryptHash sh1) == (ScryptHash sh2) = 58 | foreignPtrEq 59 | (Foreign.castForeignPtr @CChar @CUChar sh1) 60 | (Foreign.castForeignPtr @CChar @CUChar sh2) 61 | cryptoPWHashScryptSalsa208SHA256StrBytes 62 | 63 | -- | @since 0.0.1.0 64 | instance Ord ScryptHash where 65 | compare (ScryptHash sh1) (ScryptHash sh2) = 66 | foreignPtrOrd 67 | (Foreign.castForeignPtr @CChar @CUChar sh1) 68 | (Foreign.castForeignPtr @CChar @CUChar sh2) 69 | cryptoPWHashScryptSalsa208SHA256StrBytes 70 | 71 | -- | @since 0.0.1.0 72 | instance Show ScryptHash where 73 | show = Text.unpack . scryptHashToText 74 | 75 | -- | @since 0.0.1.0 76 | instance Display ScryptHash where 77 | displayBuilder = Builder.fromText . scryptHashToText 78 | 79 | -- | Hash the password with the Scrypt algorithm and a set of pre-defined parameters. 80 | -- 81 | -- The hash is encoded in a human-readable format that includes: 82 | -- 83 | -- * The result of a memory-hard, CPU-intensive hash function applied to the password; 84 | -- * The automatically generated salt used for the previous computation; 85 | -- * The other parameters required to verify the password, including the algorithm 86 | -- identifier, its version, opslimit, and memlimit. 87 | -- 88 | -- Example output: "$7$C6..../....dLONLMz8YfO/.EKvzwOeqWVVLmXg62MC.hL1m1sYtO/$X9eNjVxdD4jHAhOVid3OLzNkpv6ADJSAXygOxXqGHg7\NUL" 89 | -- 90 | -- @since 0.0.1.0 91 | scryptHashPassword :: StrictByteString -> IO ScryptHash 92 | scryptHashPassword bytestring = do 93 | unsafeUseAsCStringLen bytestring $ \(cString, cStringLen) -> do 94 | hashForeignPtr <- mallocForeignPtrBytes (fromIntegral cryptoPWHashScryptSalsa208SHA256StrBytes) 95 | withForeignPtr hashForeignPtr $ \hashPtr -> 96 | void $ 97 | cryptoPWHashScryptSalsa208SHA256Str 98 | hashPtr 99 | cString 100 | (fromIntegral cStringLen) 101 | (fromIntegral cryptoPWHashScryptSalsa208SHA256OpsLimitInteractive) 102 | cryptoPWHashScryptSalsa208SHA256MemLimitInteractive 103 | pure $ ScryptHash hashForeignPtr 104 | 105 | -- | Verify a hashed password against a password verification string. 106 | -- This returns True if successful. 107 | -- 108 | -- @since 0.0.1.0 109 | scryptVerifyPassword :: StrictByteString -> ScryptHash -> IO Bool 110 | scryptVerifyPassword bytestring (ScryptHash sh) = do 111 | unsafeUseAsCStringLen bytestring $ \(cString, cStringLen) -> do 112 | withForeignPtr sh $ \scryptHash -> do 113 | result <- 114 | cryptoPWHashScryptSalsa208SHA256StrVerify 115 | scryptHash 116 | cString 117 | (fromIntegral cStringLen) 118 | return (result == 0) 119 | 120 | -- | Convert a 'ScryptHash' to a binary 'StrictByteString' in constant time. 121 | -- 122 | -- @since 0.0.1.0 123 | scryptHashToByteString :: ScryptHash -> StrictByteString 124 | scryptHashToByteString (ScryptHash fPtr) = 125 | binaryToHex (Foreign.castForeignPtr @CChar @CUChar fPtr) cryptoPWHashScryptSalsa208SHA256StrBytes 126 | 127 | -- | Convert a 'ScryptHash' to a hexadecimal-encoded 'Text'. 128 | -- 129 | -- @since 0.0.1.0 130 | scryptHashToText :: ScryptHash -> Text 131 | scryptHashToText = Text.decodeASCII . scryptHashToByteString 132 | 133 | -- | Convert an ASCII-encoded password hash to a 'ScryptHash' 134 | -- 135 | -- This function does not perform ASCII validation. 136 | -- 137 | -- @since 0.0.1.0 138 | asciiByteStringToScryptHash :: StrictByteString -> ScryptHash 139 | asciiByteStringToScryptHash textualHash = 140 | let (fPtr, _length) = BS.toForeignPtr0 textualHash 141 | in ScryptHash (castForeignPtr @Word8 @CChar fPtr) 142 | 143 | -- | Convert an ASCII-encoded password hash to a 'ScryptHash' 144 | -- 145 | -- This function does not perform ASCII validation. 146 | -- 147 | -- @since 0.0.1.0 148 | asciiTextToScryptHash :: Text -> ScryptHash 149 | asciiTextToScryptHash = asciiByteStringToScryptHash . Text.encodeUtf8 150 | -------------------------------------------------------------------------------- /sel/src/Sel/SecretKey/Authentication.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE DerivingVia #-} 3 | {-# LANGUAGE ScopedTypeVariables #-} 4 | {-# LANGUAGE TypeApplications #-} 5 | 6 | -- | 7 | -- 8 | -- Module: Sel.SecretKey.Authentication 9 | -- Description: Authentication with HMAC-SHA512-256 10 | -- Maintainer: The Haskell Cryptography Group 11 | -- Portability: GHC only 12 | module Sel.SecretKey.Authentication 13 | ( -- ** Introduction 14 | -- $introduction 15 | 16 | -- ** Usage 17 | -- $usage 18 | 19 | -- ** Operations 20 | authenticate 21 | , verify 22 | 23 | -- ** Authentication key 24 | , AuthenticationKey 25 | , newAuthenticationKey 26 | , authenticationKeyFromHexByteString 27 | , unsafeAuthenticationKeyToHexByteString 28 | 29 | -- ** Authentication tag 30 | , AuthenticationTag 31 | , authenticationTagToHexByteString 32 | , authenticationTagFromHexByteString 33 | ) where 34 | 35 | import Control.Monad (void, when) 36 | import Data.ByteString (StrictByteString) 37 | import qualified Data.ByteString as BS 38 | import qualified Data.ByteString.Base16 as Base16 39 | import qualified Data.ByteString.Internal as BS 40 | import qualified Data.ByteString.Unsafe as BS 41 | import Data.Text (Text) 42 | import qualified Data.Text as Text 43 | import Data.Text.Display (Display, OpaqueInstance (..), ShowInstance (..)) 44 | import Foreign (ForeignPtr) 45 | import qualified Foreign 46 | import Foreign.C (CChar, CUChar, CULLong, throwErrno) 47 | import LibSodium.Bindings.CryptoAuth 48 | ( cryptoAuth 49 | , cryptoAuthBytes 50 | , cryptoAuthKeyBytes 51 | , cryptoAuthKeygen 52 | , cryptoAuthVerify 53 | ) 54 | import LibSodium.Bindings.SecureMemory 55 | import System.IO.Unsafe (unsafeDupablePerformIO) 56 | 57 | import Sel.Internal 58 | import Sel.Internal.Sodium (binaryToHex) 59 | 60 | -- $introduction 61 | -- The 'authenticate' function computes an authentication tag for a message and a secret key, 62 | -- and provides a way to verify that a given tag is valid for a given message and a key. 63 | -- 64 | -- The function computing the tag deterministic: the same @(message, key)@ tuple will always 65 | -- produce the same output. However, even if the message is public, knowing the key is required 66 | -- in order to be able to compute a valid tag. 67 | -- Therefore, the key should remain confidential. The tag, however, can be public. 68 | 69 | -- $usage 70 | -- 71 | -- > import Sel.SecretKey.Authentication qualified as Auth 72 | -- > import Sel (secureMain) 73 | -- > 74 | -- > main = secureMain $ do 75 | -- > -- The parties agree on a shared secret key 76 | -- > authKey <- Auth.newAuthenticationKey 77 | -- > -- An authentication tag is computed for the message by the server 78 | -- > let message = "Hello, world!" 79 | -- > tag = Auth.authenticate message 80 | -- > -- The server sends the message and its authentication tag 81 | -- > -- […] 82 | -- > -- The recipient of the message uses the shared secret to validate the message's tag 83 | -- > Auth.verify tag authKey message 84 | -- > -- => True 85 | 86 | -- | Compute an authentication tag for a message with a secret key shared by all parties. 87 | -- 88 | -- @since 0.0.1.0 89 | authenticate 90 | :: StrictByteString 91 | -- ^ Message to authenticate 92 | -> AuthenticationKey 93 | -- ^ Secret key for authentication 94 | -> AuthenticationTag 95 | -- ^ Cryptographic tag for authentication 96 | authenticate message (AuthenticationKey authenticationKeyForeignPtr) = unsafeDupablePerformIO $ 97 | BS.unsafeUseAsCStringLen message $ \(cString, cStringLen) -> do 98 | authenticationTagForeignPtr <- 99 | Foreign.mallocForeignPtrBytes 100 | (fromIntegral cryptoAuthBytes) 101 | Foreign.withForeignPtr authenticationTagForeignPtr $ \authTagPtr -> 102 | Foreign.withForeignPtr authenticationKeyForeignPtr $ \authKeyPtr -> 103 | void $ 104 | cryptoAuth 105 | authTagPtr 106 | (Foreign.castPtr @CChar @CUChar cString) 107 | (fromIntegral @Int @CULLong cStringLen) 108 | authKeyPtr 109 | pure $ AuthenticationTag authenticationTagForeignPtr 110 | 111 | -- | Verify that the tag is valid for the provided message and secret key. 112 | -- 113 | -- @since 0.0.1.0 114 | verify 115 | :: AuthenticationTag 116 | -> AuthenticationKey 117 | -> StrictByteString 118 | -> Bool 119 | verify (AuthenticationTag tagForeignPtr) (AuthenticationKey keyForeignPtr) message = unsafeDupablePerformIO $ 120 | BS.unsafeUseAsCStringLen message $ \(cString, cStringLen) -> 121 | Foreign.withForeignPtr tagForeignPtr $ \authTagPtr -> 122 | Foreign.withForeignPtr keyForeignPtr $ \authKeyPtr -> do 123 | result <- 124 | cryptoAuthVerify 125 | authTagPtr 126 | (Foreign.castPtr @CChar @CUChar cString) 127 | (fromIntegral @Int @CULLong cStringLen) 128 | authKeyPtr 129 | pure $ result == 0 130 | 131 | -- | A secret authentication key of size 'cryptoAuthKeyBytes'. 132 | -- 133 | -- @since 0.0.1.0 134 | newtype AuthenticationKey = AuthenticationKey (ForeignPtr CUChar) 135 | deriving 136 | ( Display 137 | -- ^ @since 0.0.1.0 138 | -- > display authenticatonKey == "[REDACTED]" 139 | ) 140 | via (OpaqueInstance "[REDACTED]" AuthenticationKey) 141 | 142 | -- | 143 | -- 144 | -- @since 0.0.1.0 145 | instance Eq AuthenticationKey where 146 | (AuthenticationKey hk1) == (AuthenticationKey hk2) = 147 | foreignPtrEqConstantTime hk1 hk2 cryptoAuthKeyBytes 148 | 149 | -- | 150 | -- 151 | -- @since 0.0.1.0 152 | instance Ord AuthenticationKey where 153 | compare (AuthenticationKey hk1) (AuthenticationKey hk2) = 154 | foreignPtrOrdConstantTime hk1 hk2 cryptoAuthKeyBytes 155 | 156 | -- | > show authenticationKey == "[REDACTED]" 157 | -- 158 | -- @since 0.0.1.0 159 | instance Show AuthenticationKey where 160 | show _ = "[REDACTED]" 161 | 162 | -- | Generate a new random secret key. 163 | -- 164 | -- @since 0.0.1.0 165 | newAuthenticationKey :: IO AuthenticationKey 166 | newAuthenticationKey = newAuthenticationKeyWith cryptoAuthKeygen 167 | 168 | -- | Prepare memory for a 'AuthenticationKey' and use the provided action to fill it. 169 | -- 170 | -- Memory is allocated with 'LibSodium.Bindings.SecureMemory.sodiumMalloc' 171 | -- (see the note attached there). 172 | -- A finalizer is run when the key is goes out of scope. 173 | newAuthenticationKeyWith :: (Foreign.Ptr CUChar -> IO ()) -> IO AuthenticationKey 174 | newAuthenticationKeyWith action = do 175 | ptr <- sodiumMalloc cryptoAuthKeyBytes 176 | when (ptr == Foreign.nullPtr) $ do 177 | throwErrno "sodium_malloc" 178 | 179 | fPtr <- Foreign.newForeignPtr_ ptr 180 | Foreign.addForeignPtrFinalizer finalizerSodiumFree fPtr 181 | action ptr 182 | pure $ AuthenticationKey fPtr 183 | 184 | -- | Create an 'AuthenticationKey' from a binary 'StrictByteString' that you have obtained on your own, 185 | -- usually from the network or disk. 186 | -- 187 | -- The input secret key, once decoded from base16, must be of length 188 | -- 'cryptoAuthKeyBytes'. 189 | -- 190 | -- @since 0.0.1.0 191 | authenticationKeyFromHexByteString :: StrictByteString -> Either Text AuthenticationKey 192 | authenticationKeyFromHexByteString hexKey = unsafeDupablePerformIO $ 193 | case Base16.decodeBase16Untyped hexKey of 194 | Right bytestring -> 195 | if BS.length bytestring == fromIntegral cryptoAuthKeyBytes 196 | then BS.unsafeUseAsCStringLen bytestring $ \(outsideAuthenticationKeyPtr, _) -> 197 | fmap Right $ 198 | newAuthenticationKeyWith $ \authenticationKeyPtr -> 199 | Foreign.copyArray 200 | (Foreign.castPtr @CUChar @CChar authenticationKeyPtr) 201 | outsideAuthenticationKeyPtr 202 | (fromIntegral cryptoAuthKeyBytes) 203 | else pure $ Left $ Text.pack "Authentication Key is too short" 204 | Left msg -> pure $ Left msg 205 | 206 | -- | Convert a 'AuthenticationKey to a hexadecimal-encoded 'StrictByteString' in constant time. 207 | -- 208 | -- ⚠️ Be prudent as to where you store it! 209 | -- 210 | -- @since 0.0.1.0 211 | unsafeAuthenticationKeyToHexByteString :: AuthenticationKey -> StrictByteString 212 | unsafeAuthenticationKeyToHexByteString (AuthenticationKey authenticationKeyForeignPtr) = 213 | binaryToHex authenticationKeyForeignPtr cryptoAuthKeyBytes 214 | 215 | -- | A secret authentication key of size 'cryptoAuthBytes'. 216 | -- 217 | -- @since 0.0.1.0 218 | newtype AuthenticationTag = AuthenticationTag (ForeignPtr CUChar) 219 | deriving 220 | ( Display 221 | -- ^ @since 0.0.1.0 222 | ) 223 | via (ShowInstance AuthenticationTag) 224 | 225 | -- | 226 | -- 227 | -- @since 0.0.1.0 228 | instance Eq AuthenticationTag where 229 | (AuthenticationTag hk1) == (AuthenticationTag hk2) = 230 | foreignPtrEqConstantTime hk1 hk2 cryptoAuthBytes 231 | 232 | -- | 233 | -- 234 | -- @since 0.0.1.0 235 | instance Ord AuthenticationTag where 236 | compare (AuthenticationTag hk1) (AuthenticationTag hk2) = 237 | foreignPtrOrdConstantTime hk1 hk2 cryptoAuthBytes 238 | 239 | -- | 240 | -- 241 | -- @since 0.0.1.0 242 | instance Show AuthenticationTag where 243 | show = BS.unpackChars . authenticationTagToHexByteString 244 | 245 | -- | Convert an 'AuthenticationTag' to a hexadecimal-encoded 'StrictByteString' in constant time. 246 | -- 247 | -- @since 0.0.1.0 248 | authenticationTagToHexByteString :: AuthenticationTag -> StrictByteString 249 | authenticationTagToHexByteString (AuthenticationTag fPtr) = 250 | binaryToHex fPtr cryptoAuthBytes 251 | 252 | -- | Create an 'AuthenticationTag' from a binary 'StrictByteString' that you have obtained on your own, 253 | -- usually from the network or disk. 254 | -- 255 | -- The input secret key, once decoded from base16, must be of length 256 | -- 'cryptoAuthBytes'. 257 | -- 258 | -- @since 0.0.1.0 259 | authenticationTagFromHexByteString :: StrictByteString -> Either Text AuthenticationTag 260 | authenticationTagFromHexByteString hexTag = unsafeDupablePerformIO $ 261 | case Base16.decodeBase16Untyped hexTag of 262 | Right bytestring -> 263 | if BS.length bytestring >= fromIntegral cryptoAuthBytes 264 | then BS.unsafeUseAsCStringLen bytestring $ \(outsideTagPtr, outsideTagLength) -> do 265 | hashForeignPtr <- BS.mallocByteString @CChar outsideTagLength -- The foreign pointer that will receive the hash data. 266 | Foreign.withForeignPtr hashForeignPtr $ \hashPtr -> 267 | -- We copy bytes from 'outsideTagPtr' to 'hashPtr'. 268 | Foreign.copyArray hashPtr outsideTagPtr outsideTagLength 269 | pure $ 270 | Right $ 271 | AuthenticationTag 272 | (Foreign.castForeignPtr @CChar @CUChar hashForeignPtr) 273 | else pure $ Left $ Text.pack "Hash is too short" 274 | Left msg -> pure $ Left msg 275 | -------------------------------------------------------------------------------- /sel/test/Main.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Test.Tasty 4 | 5 | import Sel (secureMain) 6 | import qualified Test.HMAC as HMAC 7 | import qualified Test.Hashing as Hashing 8 | import qualified Test.Hashing.Password as Password 9 | import qualified Test.Hashing.SHA2 as Hashing.SHA2 10 | import qualified Test.Hashing.Short as Short 11 | import qualified Test.PublicKey.Cipher as PublicKey.Cipher 12 | import qualified Test.PublicKey.Seal as PublicKey.Seal 13 | import qualified Test.PublicKey.Signature as PublicKey.Signature 14 | import qualified Test.Scrypt as Scrypt 15 | import qualified Test.SecretKey.Authentication as SecretKey.Authentication 16 | import qualified Test.SecretKey.Cipher as SecretKey.Cipher 17 | import qualified Test.SecretKey.Stream as SecretKey.Stream 18 | 19 | main :: IO () 20 | main = secureMain $ do 21 | defaultMain . testGroup "sel tests" $ specs 22 | 23 | specs :: [TestTree] 24 | specs = 25 | [ Hashing.spec 26 | , Password.spec 27 | , Short.spec 28 | , PublicKey.Signature.spec 29 | , PublicKey.Cipher.spec 30 | , PublicKey.Seal.spec 31 | , Hashing.SHA2.spec 32 | , SecretKey.Cipher.spec 33 | , SecretKey.Authentication.spec 34 | , SecretKey.Stream.spec 35 | , HMAC.spec 36 | , Scrypt.spec 37 | ] 38 | -------------------------------------------------------------------------------- /sel/test/Test/HMAC.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | module Test.HMAC where 4 | 5 | import Test.Tasty 6 | import Test.Tasty.HUnit 7 | 8 | import qualified Sel.HMAC.SHA256 as SHA256 9 | import qualified Sel.HMAC.SHA512 as SHA512 10 | import qualified Sel.HMAC.SHA512_256 as SHA512_256 11 | import TestUtils (assertRight) 12 | 13 | spec :: TestTree 14 | spec = 15 | testGroup 16 | "HMAC-SHA2 hashing" 17 | [ testGroup 18 | "HMAC-SHA-256" 19 | [ testCase "Single-message hashing" testSingleHMACSHA256Hashing 20 | , testCase "Multiple-message hashing" testMultipleHMAC256Hashing 21 | , testCase "Round-trip authentication key serialisation" testHMAC256AuthenticationKeySerialisation 22 | , testCase "Round-trip tag serialisation" testHMAC256AuthenticationTagSerialisation 23 | ] 24 | , testGroup 25 | "HMAC-SHA-512" 26 | [ testCase "Single-message hashing" testSingleHMACSHA512Hashing 27 | , testCase "Multiple-message hashing" testMultipleHMAC512Hashing 28 | , testCase "Round-trip authentication key serialisation" testHMAC512AuthenticationKeySerialisation 29 | , testCase "Round-trip tag serialisation" testHMAC512AuthenticationTagSerialisation 30 | ] 31 | , testGroup 32 | "HMAC-SHA-512-256" 33 | [ testCase "Single-message hashing" testSingleHMACSHA512_256Hashing 34 | , testCase "Multiple-message hashing" testMultipleHMAC512_256Hashing 35 | , testCase "Round-trip authentication key serialisation" testHMAC512_256AuthenticationKeySerialisation 36 | , testCase "Round-trip tag serialisation" testHMAC512_256AuthenticationTagSerialisation 37 | ] 38 | ] 39 | 40 | -- HMAC-SHA-256 41 | 42 | testSingleHMACSHA256Hashing :: Assertion 43 | testSingleHMACSHA256Hashing = do 44 | key <- SHA256.newAuthenticationKey 45 | let tag = SHA256.authenticate "Hello, world!" key 46 | assertBool "message is verified" $ 47 | SHA256.verify tag key "Hello, world!" 48 | 49 | testMultipleHMAC256Hashing :: Assertion 50 | testMultipleHMAC256Hashing = do 51 | key <- assertRight $ SHA256.authenticationKeyFromHexByteString "d7fd28595f186884a88235d1d3c84f836303f58aa69496f2fd76e8a709d5224e" 52 | actual <- SHA256.withMultipart key $ \multipart -> do 53 | SHA256.updateMultipart multipart "hunter" 54 | SHA256.updateMultipart multipart "2" 55 | assertEqual 56 | "HMAC-SHA256 tag is consistent" 57 | "be884a372976dd92e819d55ea7090d0b87377b3ac0773a97a5fdc12523104c35" 58 | (SHA256.authenticationTagToHexByteString actual) 59 | 60 | testHMAC256AuthenticationKeySerialisation :: Assertion 61 | testHMAC256AuthenticationKeySerialisation = do 62 | key1 <- SHA256.newAuthenticationKey 63 | let hexKey = SHA256.unsafeAuthenticationKeyToHexByteString key1 64 | key2 <- assertRight $ SHA256.authenticationKeyFromHexByteString hexKey 65 | assertEqual "Roundtripping authentication key" key1 key2 66 | 67 | testHMAC256AuthenticationTagSerialisation :: Assertion 68 | testHMAC256AuthenticationTagSerialisation = do 69 | key <- SHA256.newAuthenticationKey 70 | let tag1 = SHA256.authenticate "Hello, world!" key 71 | let hexTag = SHA256.authenticationTagToHexByteString tag1 72 | tag2 <- assertRight $ SHA256.authenticationTagFromHexByteString hexTag 73 | assertEqual "Roundtripping authentication key" tag1 tag2 74 | 75 | -- HMAC-SHA-512 76 | 77 | testSingleHMACSHA512Hashing :: Assertion 78 | testSingleHMACSHA512Hashing = do 79 | key <- SHA512.newAuthenticationKey 80 | let tag = SHA512.authenticate "Hello, world!" key 81 | assertBool "message is verified" $ 82 | SHA512.verify tag key "Hello, world!" 83 | 84 | testMultipleHMAC512Hashing :: Assertion 85 | testMultipleHMAC512Hashing = do 86 | key <- assertRight $ SHA512.authenticationKeyFromHexByteString "d7fd28595f186884a88235d1d3c84f836303f58aa69496f2fd76e8a709d5224e" 87 | actual <- SHA512.withMultipart key $ \multipart -> do 88 | SHA512.updateMultipart multipart "hunter" 89 | SHA512.updateMultipart multipart "2" 90 | assertEqual 91 | "HMAC-SHA512 tag is consistent" 92 | "7aad3ea0ca427425ba2fc3cf8078d31e94a62483b7ead624825f9a3fe36bbf5aaf8276e8876faef1a84226e439466774ebc7062495b19a6811cc376bfcccede0" 93 | (SHA512.authenticationTagToHexByteString actual) 94 | 95 | testHMAC512AuthenticationKeySerialisation :: Assertion 96 | testHMAC512AuthenticationKeySerialisation = do 97 | key1 <- SHA512.newAuthenticationKey 98 | let hexKey = SHA512.unsafeAuthenticationKeyToHexByteString key1 99 | key2 <- assertRight $ SHA512.authenticationKeyFromHexByteString hexKey 100 | assertEqual "Roundtripping authentication key" key1 key2 101 | 102 | testHMAC512AuthenticationTagSerialisation :: Assertion 103 | testHMAC512AuthenticationTagSerialisation = do 104 | key <- SHA512.newAuthenticationKey 105 | let tag1 = SHA512.authenticate "Hello, world!" key 106 | let hexTag = SHA512.authenticationTagToHexByteString tag1 107 | tag2 <- assertRight $ SHA512.authenticationTagFromHexByteString hexTag 108 | assertEqual "Roundtripping authentication key" tag1 tag2 109 | 110 | -- HMAC-SHA-512-256 111 | 112 | testSingleHMACSHA512_256Hashing :: Assertion 113 | testSingleHMACSHA512_256Hashing = do 114 | key <- SHA512_256.newAuthenticationKey 115 | let tag = SHA512_256.authenticate "Hello, world!" key 116 | assertBool "message is verified" $ 117 | SHA512_256.verify tag key "Hello, world!" 118 | 119 | testMultipleHMAC512_256Hashing :: Assertion 120 | testMultipleHMAC512_256Hashing = do 121 | key <- assertRight $ SHA512_256.authenticationKeyFromHexByteString "d7fd28595f186884a88235d1d3c84f836303f58aa69496f2fd76e8a709d5224e" 122 | actual <- SHA512_256.withMultipart key $ \multipart -> do 123 | SHA512_256.updateMultipart multipart "hunter" 124 | SHA512_256.updateMultipart multipart "2" 125 | assertEqual 126 | "HMAC-SHA512_256 tag is consistent" 127 | "7aad3ea0ca427425ba2fc3cf8078d31e94a62483b7ead624825f9a3fe36bbf5a" 128 | (SHA512_256.authenticationTagToHexByteString actual) 129 | 130 | testHMAC512_256AuthenticationKeySerialisation :: Assertion 131 | testHMAC512_256AuthenticationKeySerialisation = do 132 | key1 <- SHA512_256.newAuthenticationKey 133 | let hexKey = SHA512_256.unsafeAuthenticationKeyToHexByteString key1 134 | key2 <- assertRight $ SHA512_256.authenticationKeyFromHexByteString hexKey 135 | assertEqual "Roundtripping authentication key" key1 key2 136 | 137 | testHMAC512_256AuthenticationTagSerialisation :: Assertion 138 | testHMAC512_256AuthenticationTagSerialisation = do 139 | key <- SHA512_256.newAuthenticationKey 140 | let tag1 = SHA512_256.authenticate "Hello, world!" key 141 | let hexTag = SHA512_256.authenticationTagToHexByteString tag1 142 | tag2 <- assertRight $ SHA512_256.authenticationTagFromHexByteString hexTag 143 | assertEqual "Roundtripping authentication key" tag1 tag2 144 | -------------------------------------------------------------------------------- /sel/test/Test/Hashing.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | {-# LANGUAGE ScopedTypeVariables #-} 3 | 4 | module Test.Hashing where 5 | 6 | import Control.Monad (void) 7 | import qualified Data.ByteString.Unsafe as BS 8 | import Foreign hiding (void) 9 | import Foreign.C 10 | import LibSodium.Bindings.GenericHashing (cryptoGenericHash, cryptoGenericHashBytes) 11 | import Test.Tasty 12 | import Test.Tasty.HUnit 13 | 14 | import qualified Sel.Hashing as Hashing 15 | 16 | spec :: TestTree 17 | spec = 18 | testGroup 19 | "Generic hashing tests" 20 | [ testCase "cryptoGenericHash without key" testCryptoGenericHashWithoutKey 21 | , testCase "cryptoGenericHash with key" testCryptoGenericHashWithKey 22 | , testCase "Multi-part hashing" testMultipartHahsing 23 | ] 24 | 25 | testCryptoGenericHashWithoutKey :: Assertion 26 | testCryptoGenericHashWithoutKey = do 27 | let expected = Hashing.hashToHexByteString $ Hashing.hashByteString Nothing "test test" 28 | assertEqual 29 | "Hashed test string is consistent without key" 30 | expected 31 | "7f3dc1170e7017a1643d84d102429c4c7aec4ca99c016c32af18af997fed51f1" 32 | 33 | testCryptoGenericHashWithKey :: Assertion 34 | testCryptoGenericHashWithKey = 35 | let key = "af7d1575690407317bf93723a8d1dca5" 36 | msg = "Test Test" :: String 37 | in withCStringLen msg $ \(cString, cstringLength) -> 38 | allocaBytes (fromIntegral cryptoGenericHashBytes) $ \outPtr -> 39 | BS.unsafeUseAsCStringLen key $ \(keyPtr, keyLength) -> do 40 | void $ 41 | cryptoGenericHash 42 | outPtr 43 | cryptoGenericHashBytes 44 | (castPtr cString :: Ptr CUChar) 45 | (fromIntegral cstringLength) 46 | (castPtr keyPtr) 47 | (fromIntegral keyLength) 48 | out <- peekCStringLen (castPtr outPtr, cstringLength) 49 | assertEqual 50 | "Hashed test string is consistent with key" 51 | "<|1" 52 | out 53 | 54 | testMultipartHahsing :: Assertion 55 | testMultipartHahsing = do 56 | hashKey <- Hashing.newHashKey 57 | let expectedHash = Hashing.hashByteString (Just hashKey) "test test" 58 | actualHash <- Hashing.withMultipart (Just hashKey) $ \multipartState -> do 59 | let message1 = "test " 60 | Hashing.updateMultipart multipartState message1 61 | let message2 = "test" 62 | Hashing.updateMultipart multipartState message2 63 | assertEqual 64 | "Hash remains the same when using multipart" 65 | (Hashing.hashToHexByteString expectedHash) 66 | (Hashing.hashToHexByteString actualHash) 67 | -------------------------------------------------------------------------------- /sel/test/Test/Hashing/Password.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | module Test.Hashing.Password where 4 | 5 | import Data.Function (on) 6 | import Data.Maybe (isNothing) 7 | import Data.Text (Text) 8 | import qualified Data.Text as Text 9 | import Test.Tasty 10 | import Test.Tasty.HUnit 11 | 12 | import qualified Sel.Hashing.Password as Sel 13 | 14 | spec :: TestTree 15 | spec = 16 | testGroup 17 | "Password hashing tests" 18 | [ testCase "Round-trip test for password hashing" testRoundtripHash 19 | , testCase "Consistent password hashing with salt" testHashPasswordWSalt 20 | , testCase "ASCII representation" testASCIIRepresentation 21 | ] 22 | 23 | testRoundtripHash :: Assertion 24 | testRoundtripHash = do 25 | let password = "hunter2" :: Text 26 | passwordHash <- Sel.hashText password 27 | let passwordHash' = Sel.asciiByteStringToPasswordHash $ Sel.passwordHashToByteString passwordHash 28 | 29 | assertEqual 30 | "Original hash and hash from bytestring are the same" 31 | passwordHash 32 | passwordHash' 33 | 34 | assertBool 35 | "Password hashing is consistent" 36 | (Sel.verifyText passwordHash "hunter2") 37 | 38 | assertBool 39 | "Password hashing is consistent" 40 | (Sel.verifyText passwordHash' "hunter2") 41 | 42 | testHashPasswordWSalt :: Assertion 43 | testHashPasswordWSalt = do 44 | let hashWSalt s = Sel.hashByteStringWithParams Sel.defaultArgon2Params s 45 | password = "hunter2" 46 | cmpPWHashes = on (==) Sel.passwordHashToByteString 47 | 48 | salt1 <- Sel.genSalt 49 | let hashOrig = hashWSalt salt1 password 50 | hashOrig' = hashWSalt salt1 password 51 | assertBool 52 | "Password hashing with salt is consistent" 53 | (cmpPWHashes hashOrig hashOrig') 54 | 55 | hashWoSalt <- Sel.hashByteString password 56 | assertBool 57 | "Password hashing with salt differs from without" 58 | (not $ cmpPWHashes hashOrig hashWoSalt) 59 | 60 | salt2 <- Sel.genSalt 61 | let hashWNewSalt = hashWSalt salt2 password 62 | assertBool 63 | "Password hashing differs with a new salt" 64 | (not $ cmpPWHashes hashOrig hashWNewSalt) 65 | 66 | assertBool 67 | "Bogus salt ByteString fails to generate Salt" 68 | (isNothing (Sel.hexByteStringToSalt "deadbeef")) 69 | 70 | testASCIIRepresentation :: Assertion 71 | testASCIIRepresentation = do 72 | hash <- Sel.hashByteString "hunter3" 73 | let textHash = Sel.passwordHashToText hash 74 | assertBool 75 | "Textual representation is stable using passwordHashToText" 76 | ("$argon2id$v=19$m=262144,t=3,p=1$" `Text.isPrefixOf` textHash) 77 | 78 | let bsHash = Sel.passwordHashToByteString hash 79 | let hash2 = Sel.asciiByteStringToPasswordHash bsHash 80 | assertEqual 81 | "Can import hash" 82 | hash2 83 | hash 84 | -------------------------------------------------------------------------------- /sel/test/Test/Hashing/SHA2.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | module Test.Hashing.SHA2 where 4 | 5 | import Data.Text (Text) 6 | import Test.Tasty 7 | import Test.Tasty.HUnit 8 | 9 | import qualified Sel.Hashing.SHA256 as SHA256 10 | import qualified Sel.Hashing.SHA512 as SHA512 11 | 12 | spec :: TestTree 13 | spec = 14 | testGroup 15 | "SHA2 hashing" 16 | [ testGroup 17 | "SHA-256" 18 | [ testCase "SHA-256 single-message hashing" testSingleHashSHA256 19 | , testCase "SHA-256 multi-part message hashing" testMultipartHashSH256 20 | ] 21 | , testGroup 22 | "SHA-512" 23 | [ testCase "SHA-512 single-message hashing" testSingleHashSHA512 24 | , testCase "SHA-512 multi-part message hashing" testMultipartHashSH512 25 | ] 26 | ] 27 | 28 | testSingleHashSHA512 :: Assertion 29 | testSingleHashSHA512 = do 30 | let password = "hunter2" :: Text 31 | actual = SHA512.hashText password 32 | assertEqual 33 | "SHA-512 hashing is consistent" 34 | (SHA512.hashToHexByteString actual) 35 | "6b97ed68d14eb3f1aa959ce5d49c7dc612e1eb1dafd73b1e705847483fd6a6c809f2ceb4e8df6ff9984c6298ff0285cace6614bf8daa9f0070101b6c89899e22" 36 | 37 | testMultipartHashSH512 :: Assertion 38 | testMultipartHashSH512 = do 39 | actual <- SHA512.withMultipart $ \multipart -> do 40 | SHA512.updateMultipart multipart "hunter" 41 | SHA512.updateMultipart multipart "2" 42 | assertEqual 43 | "SHA-512 hashing is consistent" 44 | (SHA512.hashToHexByteString actual) 45 | "6b97ed68d14eb3f1aa959ce5d49c7dc612e1eb1dafd73b1e705847483fd6a6c809f2ceb4e8df6ff9984c6298ff0285cace6614bf8daa9f0070101b6c89899e22" 46 | 47 | testSingleHashSHA256 :: Assertion 48 | testSingleHashSHA256 = do 49 | let password = "hunter2" :: Text 50 | actual = SHA256.hashText password 51 | assertEqual 52 | "SHA-256 hashing is consistent" 53 | (SHA256.hashToHexByteString actual) 54 | "f52fbd32b2b3b86ff88ef6c490628285f482af15ddcb29541f94bcf526a3f6c7" 55 | 56 | testMultipartHashSH256 :: Assertion 57 | testMultipartHashSH256 = do 58 | actual <- SHA256.withMultipart $ \multipart -> do 59 | SHA256.updateMultipart multipart "hunter" 60 | SHA256.updateMultipart multipart "2" 61 | assertEqual 62 | "SHA-256 hashing is consistent" 63 | (SHA256.hashToHexByteString actual) 64 | "f52fbd32b2b3b86ff88ef6c490628285f482af15ddcb29541f94bcf526a3f6c7" 65 | -------------------------------------------------------------------------------- /sel/test/Test/Hashing/Short.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | module Test.Hashing.Short where 4 | 5 | import Data.Maybe (fromJust) 6 | import Data.Text (Text) 7 | import Test.Tasty 8 | import Test.Tasty.HUnit 9 | 10 | import qualified Sel.Hashing.Short as Short 11 | 12 | spec :: TestTree 13 | spec = 14 | testGroup 15 | "Password hashing tests" 16 | [ testCase "Hash a short string with a known salt" testHashPassword 17 | ] 18 | 19 | testHashPassword :: Assertion 20 | testHashPassword = do 21 | let key = fromJust $ Short.hexTextToShortHashKey "9301a3c5eedf2d783b72dc41fb907964" 22 | let input = "kwak kwak" :: Text 23 | let hash = Short.hashText key input 24 | assertEqual 25 | "input hashing is consistent" 26 | (Short.shortHashToHexText hash) 27 | "d50bb18bee915f21a30e6ea555c34546" 28 | -------------------------------------------------------------------------------- /sel/test/Test/PublicKey/Cipher.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | module Test.PublicKey.Cipher where 4 | 5 | import Test.Tasty 6 | import Test.Tasty.HUnit 7 | 8 | import Sel.PublicKey.Cipher 9 | import TestUtils 10 | 11 | spec :: TestTree 12 | spec = 13 | testGroup 14 | "Public Key Cipher tests" 15 | [ testCase "Encrypt a message with public-key encryption" testEncryptMessage 16 | , testCase "Round-trip nonce serialisation" testNonceSerdeRoundtrip 17 | , testCase "Round-trip keys serialisation" testKeysSerdeRoundtrip 18 | , testCase "Round-trip cipher text serialisation" testCiphertextSerdeRoundtrip 19 | ] 20 | 21 | testEncryptMessage :: Assertion 22 | testEncryptMessage = do 23 | (senderPublicKey, senderSecretKey) <- newKeyPair 24 | 25 | (recipientPublicKey, recipientSecretKey) <- newKeyPair 26 | (nonce, encryptedMessage) <- encrypt "hello hello" recipientPublicKey senderSecretKey 27 | let result = decrypt encryptedMessage senderPublicKey recipientSecretKey nonce 28 | assertEqual 29 | "Message is well-opened with the correct key and nonce" 30 | (Just "hello hello") 31 | result 32 | 33 | testNonceSerdeRoundtrip :: Assertion 34 | testNonceSerdeRoundtrip = do 35 | (publicKey, secretKey) <- newKeyPair 36 | (nonce, _) <- encrypt "hello hello" publicKey secretKey 37 | let hexNonce = nonceToHexByteString nonce 38 | nonce2 <- assertRight $ nonceFromHexByteString hexNonce 39 | assertEqual "Roundtripping nonce serialisation" nonce nonce2 40 | 41 | testKeysSerdeRoundtrip :: Assertion 42 | testKeysSerdeRoundtrip = do 43 | (pk1, sk1) <- newKeyPair 44 | let hexPk = publicKeyToHexByteString pk1 45 | let hexSk = unsafeSecretKeyToHexByteString sk1 46 | (pk2, sk2) <- assertRight $ keyPairFromHexByteStrings hexPk hexSk 47 | assertEqual "Roundtripping keys serialisation" (pk1, sk1) (pk2, sk2) 48 | 49 | testCiphertextSerdeRoundtrip :: Assertion 50 | testCiphertextSerdeRoundtrip = do 51 | (publicKey, secretKey) <- newKeyPair 52 | (_, ciphertext) <- encrypt "hello hello" publicKey secretKey 53 | let hexCiphertext = ciphertextToHexByteString ciphertext 54 | ciphertext2 <- assertRight $ ciphertextFromHexByteString hexCiphertext 55 | assertEqual "Roundtripping cipher text serialisation" ciphertext ciphertext2 56 | -------------------------------------------------------------------------------- /sel/test/Test/PublicKey/Seal.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | module Test.PublicKey.Seal where 4 | 5 | import Test.Tasty 6 | import Test.Tasty.HUnit 7 | 8 | import Sel.PublicKey.Cipher 9 | import Sel.PublicKey.Seal 10 | 11 | spec :: TestTree 12 | spec = 13 | testGroup 14 | "Public Key Anonymous Sealing tests" 15 | [ testCase "Encrypt an anonymous message with public-key encryption" testEncryptMessage 16 | ] 17 | 18 | testEncryptMessage :: Assertion 19 | testEncryptMessage = do 20 | (recipientPublicKey, recipientSecretKey) <- newKeyPair 21 | encryptedMessage <- seal "hello hello" recipientPublicKey 22 | let result = open encryptedMessage recipientPublicKey recipientSecretKey 23 | assertEqual 24 | "Message is well-opened" 25 | (Just "hello hello") 26 | result 27 | -------------------------------------------------------------------------------- /sel/test/Test/PublicKey/Signature.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | module Test.PublicKey.Signature where 4 | 5 | import Test.Tasty 6 | import Test.Tasty.HUnit 7 | 8 | import Sel.PublicKey.Signature 9 | 10 | spec :: TestTree 11 | spec = 12 | testGroup 13 | "Signing tests" 14 | [ testCase "Sign a message with a public key and decrypt it with a secret key" testSignMessage 15 | ] 16 | 17 | testSignMessage :: Assertion 18 | testSignMessage = do 19 | (publicKey, secretKey) <- generateKeyPair 20 | signedMessage <- signMessage "hello hello" secretKey 21 | let result = openMessage signedMessage publicKey 22 | assertEqual 23 | "Message is well-opened with the correct key" 24 | (Just "hello hello") 25 | result 26 | -------------------------------------------------------------------------------- /sel/test/Test/Scrypt.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | {-# LANGUAGE ScopedTypeVariables #-} 3 | 4 | module Test.Scrypt where 5 | 6 | import Data.ByteString 7 | import Test.Tasty 8 | import Test.Tasty.HUnit 9 | 10 | import Sel.Scrypt 11 | 12 | spec :: TestTree 13 | spec = 14 | testGroup 15 | "Scrypt tests" 16 | [ testCase "Hash Scrypt password" testHashScrypt 17 | , testCase "Verify Scrypt password" testVerifyScrypt 18 | ] 19 | 20 | testHashScrypt :: Assertion 21 | testHashScrypt = do 22 | let hash = "This is not a real hash." :: StrictByteString 23 | scryptHashPassword hash 24 | return () 25 | 26 | testVerifyScrypt :: Assertion 27 | testVerifyScrypt = do 28 | let hash = "This is not a real hash." :: StrictByteString 29 | sh <- scryptHashPassword hash 30 | res <- scryptVerifyPassword hash sh 31 | assertBool "Verifier failed." res 32 | -------------------------------------------------------------------------------- /sel/test/Test/SecretKey/Authentication.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | module Test.SecretKey.Authentication where 4 | 5 | import Test.Tasty 6 | import Test.Tasty.HUnit 7 | 8 | import Sel.SecretKey.Authentication 9 | import TestUtils (assertRight) 10 | 11 | spec :: TestTree 12 | spec = 13 | testGroup 14 | "Secret Key Authentication tests" 15 | [ testCase "Authenticate a message with a fixed secret key" testAuthenticateMessage 16 | , testCase "Round-trip auth key serialisation" testAuthKeySerdeRoundtrip 17 | , testCase "Round-trip auth tag serialisation" testAuthTagSerdeRoundtrip 18 | ] 19 | 20 | testAuthenticateMessage :: Assertion 21 | testAuthenticateMessage = do 22 | key <- newAuthenticationKey 23 | let tag = authenticate "hello, world" key 24 | assertBool 25 | "Tag verified" 26 | (verify tag key "hello, world") 27 | 28 | testAuthKeySerdeRoundtrip :: Assertion 29 | testAuthKeySerdeRoundtrip = do 30 | expectedKey <- newAuthenticationKey 31 | let hexKey = unsafeAuthenticationKeyToHexByteString expectedKey 32 | actualKey <- assertRight $ authenticationKeyFromHexByteString hexKey 33 | assertEqual 34 | "Key is expected" 35 | expectedKey 36 | actualKey 37 | 38 | testAuthTagSerdeRoundtrip :: Assertion 39 | testAuthTagSerdeRoundtrip = do 40 | key <- newAuthenticationKey 41 | let expectedTag = authenticate "hello, world" key 42 | hexTag = authenticationTagToHexByteString expectedTag 43 | actualTag <- assertRight $ authenticationTagFromHexByteString hexTag 44 | assertEqual 45 | "Tag is expected" 46 | expectedTag 47 | actualTag 48 | -------------------------------------------------------------------------------- /sel/test/Test/SecretKey/Cipher.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | module Test.SecretKey.Cipher where 4 | 5 | import Test.Tasty 6 | import Test.Tasty.HUnit 7 | 8 | import Sel.SecretKey.Cipher 9 | import TestUtils (assertRight) 10 | 11 | spec :: TestTree 12 | spec = 13 | testGroup 14 | "Secret Key Authenticated Encryption tests" 15 | [ testCase "Encrypt a message with a secret key and a nonce" testEncryptMessage 16 | , testCase "Round-trip nonce serialisation" testNonceSerdeRoundtrip 17 | , testCase "Round-trip secret key serialisation" testSecretKeySerdeRoundtrip 18 | , testCase "Round-trip ciphertext serialisation" testCiphertextSerdeRoundtrip 19 | ] 20 | 21 | testEncryptMessage :: Assertion 22 | testEncryptMessage = do 23 | secretKey <- newSecretKey 24 | (nonce, encryptedMessage) <- encrypt "hello hello" secretKey 25 | let result = decrypt encryptedMessage secretKey nonce 26 | assertEqual 27 | "Message is well-opened with the correct key and nonce" 28 | (Just "hello hello") 29 | result 30 | 31 | testNonceSerdeRoundtrip :: Assertion 32 | testNonceSerdeRoundtrip = do 33 | secretKey <- newSecretKey 34 | (nonce, _) <- encrypt "hello hello" secretKey 35 | nonce2 <- assertRight $ nonceFromHexByteString . nonceToHexByteString $ nonce 36 | assertEqual "Roundtripping nonce" nonce nonce2 37 | 38 | testSecretKeySerdeRoundtrip :: Assertion 39 | testSecretKeySerdeRoundtrip = do 40 | secretKey <- newSecretKey 41 | secretKey2 <- assertRight $ secretKeyFromHexByteString . unsafeSecretKeyToHexByteString $ secretKey 42 | assertEqual "Roundtripping secret key" secretKey secretKey2 43 | 44 | testCiphertextSerdeRoundtrip :: Assertion 45 | testCiphertextSerdeRoundtrip = do 46 | secretKey <- newSecretKey 47 | (_, ciphertext) <- encrypt "" secretKey 48 | ciphertext2 <- assertRight $ ciphertextFromHexByteString . ciphertextToHexByteString $ ciphertext 49 | assertEqual "Roundtripping ciphertext" ciphertext ciphertext2 50 | -------------------------------------------------------------------------------- /sel/test/Test/SecretKey/Stream.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | module Test.SecretKey.Stream where 4 | 5 | import Data.ByteString (StrictByteString) 6 | import Test.Tasty 7 | import Test.Tasty.HUnit 8 | 9 | import qualified Sel.SecretKey.Stream as Stream 10 | import TestUtils 11 | 12 | spec :: TestTree 13 | spec = 14 | testGroup 15 | "Secret Key Encrypted Stream tests" 16 | [ testCase "Encrypt a stream with a secret key" testEncryptStream 17 | , testCase "Round-trip secret key serialisation" testSecretKeySerdeRoundtrip 18 | , testCase "Round-trip ciphertext serialisation" testCiphertextSerdeRoundtrip 19 | -- , testCase "Round-trip header serialisation" testHeaderSerdeRoundtrip 20 | ] 21 | 22 | testEncryptStream :: Assertion 23 | testEncryptStream = do 24 | secretKey <- Stream.newSecretKey 25 | let messages = ["Hello", "abcdf", "world"] 26 | ad = map Just [Stream.AdditionalData "Goodbye", Stream.AdditionalData "31337", Stream.AdditionalData "planet"] 27 | messagesAndAd = zip ad messages 28 | (header, ciphertexts) <- Stream.encryptList secretKey messagesAndAd 29 | let ciphertextsAndAd = zip ad ciphertexts 30 | mResult <- Stream.decryptList secretKey header ciphertextsAndAd 31 | result <- assertJust mResult 32 | 33 | assertEqual 34 | "Expected result" 35 | result 36 | messages 37 | 38 | testSecretKeySerdeRoundtrip :: Assertion 39 | testSecretKeySerdeRoundtrip = do 40 | secretKey1 <- Stream.newSecretKey 41 | let hexSecretKey1 = Stream.unsafeSecretKeyToHexByteString secretKey1 42 | secretKey2 <- assertRight $ Stream.secretKeyFromHexByteString hexSecretKey1 43 | 44 | assertEqual 45 | "The keys remain equal" 46 | secretKey1 47 | secretKey2 48 | 49 | testCiphertextSerdeRoundtrip :: Assertion 50 | testCiphertextSerdeRoundtrip = do 51 | secretKey <- Stream.newSecretKey 52 | let message = "hello" :: StrictByteString 53 | additionalData = Stream.AdditionalData "this is additional data" 54 | (_, encryptedPayload1) <- Stream.encryptStream secretKey $ \multipart -> do 55 | Stream.encryptChunk multipart Stream.Final (Just additionalData) message 56 | 57 | let hexCiphertext = Stream.ciphertextToHexByteString encryptedPayload1 58 | encryptedPayload2 <- assertRight $ Stream.ciphertextFromHexByteString hexCiphertext 59 | 60 | assertEqual 61 | "The ciphertexts remain equal" 62 | encryptedPayload1 63 | encryptedPayload2 64 | -------------------------------------------------------------------------------- /sel/test/TestUtils.hs: -------------------------------------------------------------------------------- 1 | module TestUtils where 2 | 3 | import Control.Monad.IO.Class (MonadIO, liftIO) 4 | import GHC.Stack 5 | import qualified Test.Tasty.HUnit as Test 6 | 7 | assertRight :: MonadIO m => HasCallStack => Either a b -> m b 8 | assertRight (Left _a) = liftIO $ Test.assertFailure "Test return Left instead of Right" 9 | assertRight (Right b) = pure b 10 | 11 | assertJust :: MonadIO m => HasCallStack => Maybe a -> m a 12 | assertJust Nothing = liftIO $ Test.assertFailure "Test return Nothing instead of Just" 13 | assertJust (Just b) = pure b 14 | --------------------------------------------------------------------------------