Full Code of jlouis/enacl for AI

master 46e2754a50f4 cached
58 files
262.5 KB
76.9k tokens
139 symbols
1 requests
Download .txt
Showing preview only (278K chars total). Download the full file or copy to clipboard to get everything.
Repository: jlouis/enacl
Branch: master
Commit: 46e2754a50f4
Files: 58
Total size: 262.5 KB

Directory structure:
gitextract_mbe4rx4c/

├── .envrc
├── .github/
│   └── workflows/
│       ├── Makefile
│       ├── actions.cue
│       ├── ci.yaml
│       ├── setup.cue
│       └── steps.cue
├── .gitignore
├── .vscode/
│   ├── c_cpp_properties.json
│   └── settings.json
├── CHANGELOG.md
├── CONTRIBUTORS
├── LICENSE
├── Makefile
├── README.md
├── bench/
│   ├── enacl_nif.d
│   ├── funcall_enacl.d
│   └── timing.erl
├── benchmark.sh
├── c_src/
│   ├── aead.c
│   ├── aead.h
│   ├── enacl.c
│   ├── enacl.h
│   ├── enacl_ext.c
│   ├── enacl_ext.h
│   ├── enacl_nif.c
│   ├── generichash.c
│   ├── generichash.h
│   ├── hash.c
│   ├── hash.h
│   ├── kdf.c
│   ├── kdf.h
│   ├── kx.c
│   ├── kx.h
│   ├── public.c
│   ├── public.h
│   ├── pwhash.c
│   ├── pwhash.h
│   ├── randombytes.c
│   ├── randombytes.h
│   ├── secret.c
│   ├── secret.h
│   ├── secretstream.c
│   ├── secretstream.h
│   ├── sign.c
│   └── sign.h
├── doc/
│   └── overview.edoc
├── eqc_test/
│   ├── Makefile
│   ├── enacl_eqc.erl
│   └── enacl_ext_eqc.erl
├── priv/
│   └── .dummy
├── rebar.config
├── shell.nix
├── src/
│   ├── Makefile
│   ├── enacl.app.src
│   ├── enacl.erl
│   ├── enacl_ext.erl
│   └── enacl_nif.erl
└── test/
    └── enacl_SUITE.erl

================================================
FILE CONTENTS
================================================

================================================
FILE: .envrc
================================================
eval "$(lorri direnv)"


================================================
FILE: .github/workflows/Makefile
================================================
all:
	cue export --out yaml > ci.yaml



================================================
FILE: .github/workflows/actions.cue
================================================
package actions

#Name: string
#Branches: branches: [...string]
#Tags: tags: [...string]

#On: {
	push?:         #Branches
	pull_request?: #Branches
	page_build?:   #Branches
}

#Action: "actions/checkout@v2" | "erlef/setup-beam@v1"
#Uses: {
	uses: #Action
	with?: {
		...
	}
}
#Run: {
	name: string
	run:  string
}
#Steps: #Uses | #Run

#OS_Version: *"ubuntu-latest" | "macos-latest" | "windows_latest"

#Jobs: ci: {
	name:      string
	"runs-on": string
	strategy:
		matrix: {
			otp_vsn: [...string]
			os: [...#OS_Version]
		}
	steps: [...#Steps]
}

================================================
FILE: .github/workflows/ci.yaml
================================================
name: build
on:
  push:
    branches:
    - master
  pull_request:
    branches:
    - master
jobs:
  ci:
    name: Run checks and tests over ${{matrix.otp_vsn}} and ${{matrix.os}}
    runs-on: ${{matrix.os}}
    strategy:
      matrix:
        otp_vsn:
        - "22.3"
        - "23.3"
        - "24.0"
        os:
        - ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: erlef/setup-beam@v1
      with:
        otp-version: ${{matrix.otp_vsn}}
        rebar3-version: 3.16.1
    - name: Update apt-get database
      run: sudo apt-get update
    - name: Install libsodium
      run: sudo apt-get install -y libsodium-dev
    - name: Compile source code
      run: make compile
    - name: Run the tests
      run: make tests


================================================
FILE: .github/workflows/setup.cue
================================================
package actions

name: #Name & "build"
on:   #On & {
	push: branches: [
		_branch,
	]
	pull_request: branches: [
		_branch,
	]
}

jobs: #Jobs
jobs: ci: {
	name:      "Run checks and tests over ${{matrix.otp_vsn}} and ${{matrix.os}}"
	"runs-on": "${{matrix.os}}"
	strategy: matrix: {
		otp_vsn: _versions.otp
		os: ["ubuntu-latest"]
	}
}

================================================
FILE: .github/workflows/steps.cue
================================================
package actions

// Versions for simplicity
_versions: {
	// The versions here have an underlying Debian/Ubuntu which support enough of
	// libsodium to handle what enacl provides. Older versions will fail to compile
	otp: ["22.3", "23.3", "24.0"]
	rebar3: "3.16.1"
}

_branch: "master"

jobs: ci: steps:
[
	{
		uses: "actions/checkout@v2"
	},
	{
		uses: "erlef/setup-beam@v1"
		with: {
			"otp-version":    "${{matrix.otp_vsn}}"
			"rebar3-version": _versions.rebar3
		}
	},
	{
		name: "Update apt-get database"
		run:  "sudo apt-get update"
	},
	{
		name: "Install libsodium"
		run:  "sudo apt-get install -y libsodium-dev"
	},
	{
		name: "Compile source code"
		run:  "make compile"
	},
	{
		name: "Run the tests"
		run:  "make tests"
	}]


================================================
FILE: .gitignore
================================================
.rebar
.rebar3
ebin
*.beam
*.o
*.eqc
*.so
eqc_test/.eqc-info
doc/edoc-info
doc/*.html
doc/*.png
doc/*.css
_build
/.eqc-info
priv/enacl_nif.dll
priv/enacl_nif.exp
priv/enacl_nif.lib
c_src/*.d



================================================
FILE: .vscode/c_cpp_properties.json
================================================
{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**"
            ],
            "defines": [],
            "compilerPath": "/nix/store/fb30zc52va0g99q8qgv7kx4ngq163pii-gcc-wrapper-9.3.0/bin/gcc",
            "cStandard": "c11",
            "cppStandard": "c++17",
            "intelliSenseMode": "clang-x64"
        }
    ],
    "version": 4
}

================================================
FILE: .vscode/settings.json
================================================
{
    "C_Cpp.errorSquiggles": "Disabled"
}

================================================
FILE: CHANGELOG.md
================================================
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [1.2.1]

### Fixed [1.2.1]

- Export types from the `enacl` module so it can be referenced in other parts of your system (serokell.io)

## [1.2.0]

### Fixed [1.2.0]

- `sign_verify_detached/3` The code now verifies the size of signatures in detached mode. Before
  this change, you could supply a larger binary and the code would only use the first `SIGNBYTES`
  of the binary, assuming the signature were in there. Now, it fails with a badarg if the signature
  doesn't match expectation in size.

## [1.1.1]

### Added [1.1.1]

- Introduce the ability to reload the enacl module (Bryan Paxton, @starbelly)

## [1.1.0]

### Added [1.1.0]

- Secretstream support was added to the API (Alexander Malaev)
- Add KDF functions (Nicolas Goy, @kuon)
- Add pwhash/5 specifying what algorithm to use for older compatibility (Nicolas Goy, @kuon)

### Changed [1.1.0]

- Remove rebar3_hex as a direct dependency (Bryan Paxton, @starbelly)

## [1.0.0]

### Compatibility [1.0.0]

- Some functions have been streamlined to badarg in certain cases where it made more
  sense to do so than returning back an error to the caller.
- Functions generally don't return error values for internal errors. They now raise
  exceptions when this happens. If you can't allocate a binary, there is usually not
  much the programmer can do with that information, sans crashing.
- If you used `aead_chacha20poly1305_*` functions, please read through the changelog
  carefully as we have made changes to these functions. TL;DR: look for
  `aead_chacha20poly1305_ietf_*` but note it is *not* just a simple substitution
  into your code.
- The `kx` constants have been renamed to follow libsodium one-to-one.
- All calls with `verify` now returns booleans. See `sign_verify_detached`, which
  were changed by this.
- Many constants were changed to their underlying libsodium names.

### Removed [1.0.0]

- The functions of the form `aead_chacha20poly1305_*` were removed. They implement
  the IETF variant, and the argument order for them were wrong. Also, they used
  severely limited nonce values, which is somewhat dangerous. The `..._NONCEBYTES`
  name was changed to the consistent `..._NPUBBYTES`.

### Added [1.0.0]

- Added `aead_chacha20poly1305_ietf_*` variants.
- Implement multipart signature support, by Garry Hill.
- Implement enacl:crypto_sign_seed_keypair/1, by Ole Andre Birkedal.
- Implement enacl:crypto_sign_ed25519_sk_to_pk/1, by an anonymous contribution.
- Added AEAD XChaCha20-Poly1305 support, thanks to Github/ECrownofFire.
- The Password Hash Generation functions now support memory and operations limits,
  thanks to Github/ECrownofFire.
- Implement enacl:randombytes_uint32/0. Returns a random 32bit unsigned
  integer, by means of the underlying random source.
- Implement enacl:randombytes_uniform/1. Takes up to a 32bit unsigned
  integer and produces a uniform integer in the range [0..N). Note
  that the implementation avoids the typical non-uniformness which
  would be present on a modulus operation on the nearest power-of-two
  integer.
- Added Win32 build support (Tino Breddin)
- Added a nix shell for easier development

### Changed [1.0.0]

- Started a split the C code over multiple files for easier maintenance.
- Rewrote the generichash routines to be more consistent. We are now more-or-less
  following the style of the Erlang/OTP `crypto` library. While here, make sure
  we clean up correctly and that we don't accidentally mis-ref-count data. The
  code is a bit more goto heavy, but this style is surprisingly common in C code.
- Use sodium's dynamic memory allocators. These guarantee 64bit alignment, and also
  provide guard pages around the allocation, somewhat protecting it. It adds some
  page table pressure compared to the current code, but is easier to maintain and
  much cleaner code.
- The code now rejects updates to generichash states which were already finalized.
- We now track the desired outlen of a generichash operation in the opaque NIF
  resource rather than on the Erlang side. This avoids some checks in the code,
  and streamlines a good deal of the interface.
- Split AEAD routines off from the main enacl_nif.c file
- Renamed many routines from enif_* to enacl_*. This better reflects where they live
  in the code base, and avoids pollution of the enif_* "namespace".
- Split Sign Public Key routines from the rest. Modernize the handling of contexts.
- The multi-part generic hash routines now follow the structure of the crypto
  modules multi-part constructions in API and style.
- The AEAD constructions have been streamlined so they follow the rules of libsodium
  closer than before. In particular, some dead code has been removed as a result.
- Constants are now named by their libsodium counterpart. This should make it easier
  to find the correct names given the libsodium documentation.
- Generichash now checks if a `_final` call has already happened and rejects further
  hashing on the object. The rejection is an error: if you ever do this, your code
  is definitely wrong and there is no recovery possible.

### Fixed [1.0.0]

- Fix a resource leak in generichash/sign init/update/final.
- Clang static analysis warnings (Thomas Arts).
- Replace a constant 31 with a computation from libsodium (Thomas Arts, from a security review).
- Some subtle memory leaks in the error path for kx operations were plugged.
- The multi-part generichash interface is now properly process/thread safe.
- The sign interface is now properly process/thread safe.

## [0.17.2]

### Fixed [0.17.2]

- Work around `rebar3 hex` publishing .so files

## [0.17.1]

### Fixed [0.17.1]

- Provide a fix for the `pwhash_str/x` functions. The C strings were
  not properly handled wrt. NULL-termination and what the libsodium
  library expects.

## [0.17.0]

### Added [0.17.0]

- Expose the AEAD ChaCha20 Poly1305 (IETF) functionality (Hans
  Svensson / Quviq).
- Expose Curve25519 Scalar Multiplication over a base point in the
  curve (Hans Svensson / Quviq)
- Support the pwhash_* primitives (relying on Argon2) for password
  hashing (daveed-al / Venkatakumar Srinivasan)
- Support for EQC Mini runs (Irina Guberman). The generator doesn't
  inject faults, but it does verify the positive path. This is useful
  to verify the enacl library on embedded platforms and so on.
- Support generichash functions (Venkatakumar Srinivasan / Xaptum)

### Fixed [0.17.0]

- The type specification of generichash/2 and generichash/3 was
  corrected (Technion)

### Changed [0.17.0]

- Removed the experimental feature flag `ERL_NIF_DIRTY_JOB_CPU_BOUND`.
  This breaks compatibility with older Erlang releases of dirty
  schedulers, but prepares us correctly for the new releases where the
  dirty scheduler interface is on and enabled by default (YAZ!)
- Some `size_t` entries in the C layer are now `uint` (Zane Beckwith).
  The change only affects messages of exorbitant sizes, which we think
  should be guarded against anyway, and it fixes some obvious
  compilation problems on 32 bit architectures, and to boot matches
  better against the Erlang NIF interface. We might change this later,
  but hopefully this is a change for the better.

## [0.16.0]

Bump libsodium requirement to version 1.0.12. This gives us access to
a number of functions which are added recently and thus gives us
access to implement these from libsodium.

### Added [0.16.0]

- Add kx_* functions (Alexander Malaev)
- chacha stream functions added, siphash-2-4 added, unsafe_memzero/1
  added (no attribution)

### Fixed [0.16.0]

- Do not use the dirty-scheduler test macro as it is gone.

## [0.15.0]

### Fixed [0.15.0]

- Using `enacl:sign_verify_detacted` on large iolists would fail to do
  the correct thing due to a typo. This has been corrected. Also the
  EQC tests have been extended to include large binary support to
  capture these kinds of errors in the future.

### Changed [0.15.0]

- Many dirty-scheduler tunings have been performed to make sure we
  won't block a scheduler ever.
- New benchmarks: `bench/timing.erl` together with DTrace scripts
  `bench/*.d`
- Interface simplification toward the NIF api. Only execute
  instructions directly on the scheduler if the operation *really*
  benefits from doing so.

No functional change, but the above characteristic change may mean the
library now behaves differently from what it did before. It should be
a better citizen to other libraries and other parts of the system.

## [0.14.0]

### Added [0.14.0]

- Add support for libsodiums `box_seal` functions (Amir Ghassemi Nasr)
- Add support for libsodiums `crypto_sign_detached` (Joel Stanley,
  Parnell Springmeyer)

### Changed [0.14.0]

- Switch the tag names to the form `0.14.0` rather than `v0.14.0`. For
  this release both tags are present, but from the next release on, it
  won't be the case.

## [0.13.0]

### Fixed [0.13.0]

- Quell warnings from the C code

### Added [0.13.0]

- Add Ed 25519 utility API (Alexander Færøy)
- Add FreeBSD support for the NIF compilation (Ricardo Lanziano)

## [0.12.1]

### Changed [0.12.1]

- Provide the `priv` directory for being able to properly build
  without manual intervention.

## [0.12.0]

### Added [0.12.0]

- Introduce an extension interface for various necessary extensions to
  the eNaCl system for handling the Tor network, thanks to Alexander
  Færøy (ahf).
- Introduce Curve25519 manipulations into the extension interface.
- Write (rudimentary) QuickCheck tests for the new interface, to
  verify its correctness.

## [0.11.0]

### Added [0.11.0]

- Introduce NIF layer beforenm/afternm calls.
- Introduce the API for precomputed keys (beforenm/afternm calls).
- Use test cases which tries to inject `iodata()` rather than binaries
  in all places where `iodata()` tend to be accepted.

### Fixed [0.11.0]

- Fix type for `enacl:box_open/4`. The specification was wrong which
  results in errors in other applications using enacl.

## [0.10.2]

Maintenance release. Fix some usability problems with the library.

### Fixed [0.10.2]

- Do not compile the C NIF code if there are no dirty scheduler
  support in the Erlang system (Thanks to David N. Welton)
- Fix dialyzer warnings (Thanks Anthony Ramine)
- Fix a wrong call in the timing code. Luckily, this error has not
  affected anything as it has only replaced a verification call with
  one that does not verify. In practice, the timing is roughly the
  same for both, save for a small constant factor (Thanks to the
  dialyzer)
- Improve documentation around installation/building the software.
  Hopefully it is now more prominent (Thanks to David N. Welton)

## [0.10.1]

### Added [0.10.1]

- This small patch-release provides tests for the `randombytes/1`
function call, and optimizes EQC tests to make it easier to implement
`largebinary`-support in EQC tests.
- The release also adds an (experimental) scrambling function for
hiding the internal structure of counters. This is based on an
enlarged TEA-cipher by Wheeler and Needham. It is neccessary for
correct operation of the CurveCP implementation, which is why it is
included in this library.

## [0.10.0]

Ultra-late beta; tuning for the last couple of functions which could
be nice to have.

### Added [0.10.0]

Added the function `randombytes/1` to obtain randombytes from the
operating system. The system uses the "best" applicable (P)RNG on the
target system:

- Windows: `RtlGenRandom()`
- OpenBSD, Bitrig: `arc4random()`
- Unix in general: `/dev/urandom`

Do note that on Linux and FreeBSD at the *least*, this is the best
thing you can do. Relying on `/dev/random` is almost always wrong and
gives no added security benefit. Key generation in NaCl relies on
`/dev/urandom`. Go relies on `/dev/urandom`. It is about time Erlang
does as well.

## [0.9.0]

Ultra-late beta. Code probably works, but it requires some real-world
use before it is deemed entirely stable.

Initial release.


================================================
FILE: CONTRIBUTORS
================================================
List of people who have contributed to the eNaCl source code:

Alexander Færøy
Alexander Malaev
Amir Ghassemi Nasr
Bryan Paxton
GitHub/ECrownofFire
Geller Bedoya
Jesper Louis Andersen
Joel Stanley
Konrad Zemek
Nicolas Goy
Parnell Springmeyer
Ricardo Lanziano
Tino Breddin
Venkatakumar Srinivasan



================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2014-2018 Jesper Louis Andersen

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: Makefile
================================================
REBAR=rebar3
RUN_EQC=erl -pa _build/default/lib/enacl/ebin -noshell -s enacl_eqc -s init stop

.PHONY: compile
compile:
	$(REBAR) compile

.PHONY: tests
tests:
	$(REBAR) ct

eqc_compile: compile
	erlc -o _build/default/lib/enacl/ebin eqc_test/enacl_eqc.erl

eqc_mini_compile: compile
	erlc -Dmini -o _build/default/lib/enacl/ebin eqc_test/enacl_eqc.erl

eqc_run: eqc_compile
	$(RUN_EQC)

eqc_mini_run: eqc_mini_compile
	$(RUN_EQC)

.PHONE: console
console: compile
	$(REBAR) shell

.PHONY: clean
clean:
	$(REBAR) clean


================================================
FILE: README.md
================================================
# Erlang bindings for NaCl/libsodium

This library provides bindings for the libsodium cryptographic library
for Erlang. Originally called NaCl by Bernstein, Lange and Schwabe[0],
Frank Denis took the source and made it far more portable in the
libsodium library. The enacl project is somewhat misnamed, as it uses
libsodium as the underlying driver.

## INSTALL/Requirements

* New-ish Erlang installation. Tested back to version 22.3, but version 21 *may*
  work as well.
* *Requires* the libsodium library, and has been tested with version
  1.0.18. Lower versions might work, or they might fail to compile,
  due to missing functionality. In particular, this means your libsodium installation
  must be fairly recent as well.

*Note:* If installing on systems which cuts packages into
subpackages, make sure you also get the "-dev" package containing
the header files necessary in order to compile software linking to
libsodium.

To build the software execute:

  make

or

  rebar compile

To build and run licensed eqc test execute:

  make eqc_run

To build and run eqc-mini version of test execute:

  make eqc_mini_run

## Features

* Complete NaCl library, implementing all default functionality.
* Implements a large set of additional functionality from libsodium.
  Most notably access to a proper CSPRNG random source
* Tests created by aggressive use of Erlang QuickCheck.
* NaCl is a very fast cryptographic library. That is,
  crypto-operations runs quickly on modern CPUs, with ample security
  margins. This makes it highly useful on the server-side, where
  simultaneous concurrent load on the system means encryption can have
  a considerable overhead.
* Is tested on Linux, FreeBSD and Illumos (Omnios)

This package draws heavy inspiration from "erlang-nacl" by Tony
Garnock-Jones, and started its life with a gently nod in that
direction. However, it is a rewrite and it alters lots of code from
Tony's original work.

In addition, I would like to thank Steve Vinoski, Rickard Green, and
Sverker Eriksson for providing the Dirty Scheduler API in the first
place.

## Usage

In general, consult the libsodium documentation at [Libsodium documentation](https://download.libsodium.org/doc/)

The original NaCl documentation is nowadays largely superceded by the
libsodium documentation, but it is still worth a visit [NaCl website](https://nacl.cr.yp.to)

but also note that our interface has full Edoc documentation,
generated by executing

  rebar3 doc

## Hints

In general, the primitives provided by NaCl are intermediate-level
primitives. Rather than you having to select a cipher suite, it is
selected for you, and primitives are provided at a higher level.
However, their correct use is still needed in order to be secure:

* Always make sure you obey the scheme of *nonce* values. If you ever
  reuse a nonce, and an attacker figures this out, the system will
  leak the XOR difference of messages sent with the same nonce. Given
  enough guessing, this can in turn leak the encryption stream of bits
  and every message hereafter, sent on the same keypair combination
  and reusing that nonce, will be trivially breakable.
* Use the beforenm/afternm primitives if using the `box` public-key
  encryption scheme. Precomputing the Curve25519 operations yields
  much faster operation in practice for a stream. Consult the `bench`
  directory for benchmarks in order to see how much faster it is for
  your system. The authors Core i7-4900MQ can process roughly 32
  Kilobyte data on the stream in the time it takes to do the
  Curve25519 computations. While NaCl is *fast*, this can make it even
  faster in practice.
* Encrypting very large blocks of data, several megabytes for
  instance, is problematic for two reasons. First, while the library
  attempts to avoid being a memory hog, you need at least a from-space
  and a to-space for the data, meaning you need at least double the
  memory for the operation. Furthermore, while such large blocks are
  executed on the dirty schedulers, they will never yield the DS for
  another piece of work. This means you end up blocking the dirty
  schedulers in turn. It is often better to build a framing scheme and
  encrypt data in smaller chunks, say 64 or 128 kilobytes at a time.
  In any case, it is important to measure. Especially for latency.
* The library should provide correct success type specifications. This
  means you can use the dialyzer on your code and get hints for
  incorrect usage of the library.
* Note that every "large" input to the library accepts `iodata()`
  rather than `binary()` data. The library itself will convert
  `iodata()` to binaries internally, so you don't have to do it at
  your end. It often yields simpler code since you can just build up
  an iolist of your data and shove it to the library. Key material,
  nonces and the like are generally *not* accepted as `iodata()`
  however but requires you to input binary data. This is a deliberate
  choice since most such material is not supposed to be broken up and
  constructed ever (except perhaps for the Nonce construction).
* The `enacl:randombytes/1` function provides portable access to the
  CSPRNG of your kernel. It is an *excellent* source of CSPRNG random
  data. If you need PRNG data with a seed for testing purposes, use
  the `rand` module of Erlang. The other alternative is the `crypto`
  module, which are bindings to OpenSSL with all its blessings and/or
  curses.
* Beware of timing attacks against your code! A typical area is string
  comparison, where the comparator function exits early. In that case,
  an attacker can time the response in order to guess at how many
  bytes where matched. This in turn enables some attacks where you use
  a foreign system as an oracle in order to learn the structure of a
  string, breaking the cryptograhic system in the process.

## Versions

See CHANGELOG.md

## Overview

The NaCl cryptographic library provides a number of different
cryptographic primitives. In the following, we split up the different
generic primitives and explain them briefly.

*A note on Nonces:* The crypto API makes use of "cryptographic
nonces", that is arbitrary numbers which are used only once. For these
primitives to be secure it is important to consult the NaCl
documentation on their choice. They are large values so generating
them randomly ensures security, provided the random number generator
uses a sufficiently large period. If you end up using, say, the nonce
`7` every time in communication while using the same keys, then the
security falls.

The reason you can pick the nonce values is because some uses are
better off using a nonce-construction based on monotonically
increasing numbers, while other uses do not. The advantage of a
sequence is that it can be used to reject older messages in the stream
and protect against replay attacks. So the correct use is up to the
application in many cases.

## Public Key cryptography

This implements standard Public/Secret key cryptography. The
implementation roughly consists of two major sections:

* *Authenticated encryption:* provides a `box` primitive which
  encrypts and then also authenticates a message. The reciever is only
  able to open the sealed box if they posses the secret key and the
  authentication from the sender is correct.
* *Signatures:* allows one party to sign a message (not encrypting it)
  so another party can verify the message has the right origin.

## Secret key cryptography

This implements cryptography where there is a shared secret key between parties.

* *Authenticated encryption:* provides a `secret box` primitive in
  which we can encrypt a message with a shared key `k`. The box also
  authenticates the message, so a message with an invalid key will be
  rejected as well. This protects against the application obtaining
  garbage data.
* *Encryption:* provides streams of bytes based on a Key and a Nonce.
  These streams can be used to `XOR` with a message to encrypt it. No
  authentication is provided. The API allows for the system to `XOR`
  the message for you while producing the stream.
* *Authentication:* Provides an implementation of a Message
  Authentication Code (MAC).
* *One Time Authentication:* Authenticate a message, but do so
  one-time. That is, a sender may *never* authenticate several
  messages under the same key. Otherwise an attacker can forge
  authenticators with enough time. The primitive is simpler and faster
  than the MAC authenticator however, so it is useful in some
  situations.

## Low-level functions

* *Hashing:* Cryptographically secure hashing
* *String comparison:* Implements guaranteed constant-time string
  comparisons to protect against timing attacks.

## Rationale

Doing crypto right in Erlang is not that easy. For one, the crypto
system has to be rather fast, which rules out Erlang as the main
vehicle. Second, cryptographic systems must be void of timing attacks.
This mandates we write the code in a language where we can avoid such
timing attacks, which leaves only C as a contender, more or less. The
obvious way to handle this is by the use of NIF implementations, but
most C code will run to its conclusion once set off for processing.
This is a major problem for a system which needs to keep its latency
in check. The solution taken by this library is to use the new Dirty
Scheduler API of Erlang in order to provide a safe way to handle the
long-running cryptographic processing. It keeps the cryptographic
primitives on the dirty schedulers and thus it avoids the major
problem.

Focus has first and foremost been on the correct use of dirty
schedulers, without any regard for speed. The plan is to extend the
underlying implementation, while keeping the API stable. We can
precompute keys for some operations for instance, which will yield a
speedup.

Also, while the standard `crypto` bindings in Erlang does a great job
at providing cryptographic primitives, these are based on OpenSSL,
which is known to be highly problematic in many ways. It is not as
easy to use the OpenSSL library correctly as it is with these
bindings. Rather than providing a low-level cipher suite, NaCl
provides intermediate level primitives constructed as to protect the
user against typical low-level cryptographic gotchas and problems.

## Scheduler handling

To avoid long running NIFs, the library switches to the use of dirty
schedulers for large encryption tasks. We investigated the Dirty
Scheduler switch overhead with DTrace on FreeBSD and found it to be
roughly 5μs in typical cases. Thus, we target calls taking at least
35μs is being easier to run directly on the dirty scheduler, as the
overhead for switching is thus going to be less than 15%. This means
very small operations are run directly on the BEAM scheduler, but as
soon as the operation takes a little longer, the switch overhead is
not large enough to warrant the current schedulers involvement.

In turn, some operations are *always* run on the dirty scheduler
because they take a long time in every case. This setup is far simpler
for most operations, unless the operation is performance sensitive and
allows small messages.

The tests were conducted on a Core 2 Duo machine, with newer machines
perhaps being able to switch faster. There are plans to rerun these
tests on OSX and Illumos as well, in order to investigate the numbers
on more platforms.

## Testing

Every primitive has been stress-tested through the use of Erlang
QuickCheck with both *positive* and *negative* testing. This has been
used to check against memory leaks as well as correct invocation.
Please report any error so we can extend the test cases to include a
randomized test which captures the problem so we generically catch
every problem in a given class of errors.

Positive and negative testing refers to Type I and Type II errors in
statistical testing. This means false positives—given a *valid* input
the function rejects it; as well as false negatives—given an *invalid*
input the functions fails to reject that input.

The problem however, is that while we are testing the API level, we
can't really test the strength of the cryptographic primitives. We can
verify their correctness by trying different standard correctness
tests for the primitives, verifying that the output matches the
expected one given a specific input. But there is no way we can show
that the cryptographic primitive has the strength we want. Thus, we
opted to mostly test the API and its invocation for stability.

Also, in addition to correctness, testing the system like this makes
sure we have no memory leaks as they will show themselves under the
extensive QuickCheck test cases we run. It has been verified there are
no leaks in the code.

## Notes

[0] Other people have worked on bits and pieces of NaCl. These are
just the 3 main authors. Please see the page [NaCl](http://nacl.cr.yp.to)
for the full list of authors.


================================================
FILE: bench/enacl_nif.d
================================================
/* Dirty NIF schedule overhead */
pid$target:beam.smp:schedule_dirty_cpu_nif:return
{
	s = timestamp;
}

pid$target:libsodium.so.*:randombytes:entry {
	e = timestamp;
}

pid$target:beam.smp:execute_dirty_nif:entry
/s != 0/
{
	@SchedTime = lquantize(timestamp - s, 0, 10000, 250);
	s = 0;
}

pid$target:beam.smp:execute_dirty_nif:return
{
	@ExecTime = lquantize(timestamp - e, 0, 10000, 250);
	e = 0;
	r = timestamp;
}

pid$target:beam.smp:dirty_nif_finalizer:entry
/r != 0/
{
	@ReturnTime = lquantize(timestamp - r, 0, 10000, 250);
	r = 0;
}

END
{
	printa("Scheduling overhead (nanos):%@d\n", @SchedTime);
	printa("Return overhead (nanos):%@d\n", @ReturnTime);
	printa("Exec time (nanos):%@d\n", @ExecTime);
}


================================================
FILE: bench/funcall_enacl.d
================================================
erlang*:::nif-entry
{
	funcall_entry_ts[cpu, copyinstr(arg1)] = timestamp;
}

erlang*:::nif-return
{
	@time[cpu, copyinstr(arg1)] = lquantize((timestamp - funcall_entry_ts[cpu, copyinstr(arg1)] ), 0, 60000, 1000);
}


================================================
FILE: bench/timing.erl
================================================
-module(timing).
-export([test/0]).

test() ->
    randombytes(),
    randombytes(),
    randombytes(),
    hash(),
    box_keypair(),
    box(),
    box_before_after(),
    sign_keypair(),
    sign(),
    secretbox(),
    stream(),
    auth(),
    onetime_auth(),
    scalarmult(),
    ok.

randombytes() ->
    randombytes(100*1000).

randombytes(0) -> ok;
randombytes(N) ->
    enacl:randombytes(1024),
    randombytes(N-1).

hash() ->
    B = binary:copy(<<0>>, 4096),
    hash(B, 10*1000).

hash(_B, 0) -> ok;
hash(B, N) ->
    enacl:hash(B),
    hash(B, N-1).

box_keypair() ->
    box_keypair(10*1000).

box_keypair(0) -> ok;
box_keypair(N) ->
    enacl:box_keypair(),
    box_keypair(N-1).

box() ->
    #{ public := PK1} = enacl:box_keypair(),
    #{ secret := SK2} = enacl:box_keypair(),
    B = binary:copy(<<0>>, 1),
    Nonce = binary:copy(<<0>>, enacl:box_NONCEBYTES()()),
    box(B, Nonce, PK1, SK2, 10*1000).

box(_B, _Nonce, _PK1, _SK2, 0) -> ok;
box(B, Nonce, PK1, SK2, N) ->
    enacl:box(B, Nonce, PK1, SK2),
    enacl:box_seal(B, PK1),
    box(B, Nonce, PK1, SK2, N-1).

box_before_after() ->
    #{ public := PK1 } = enacl:box_keypair(),
    #{ secret := SK2 } = enacl:box_keypair(),
    box_beforenm(PK1, SK2, 10*1000),
    R = enacl:box_beforenm(PK1, SK2),
    B = binary:copy(<<0>>, 8192),
    Nonce = binary:copy(<<0>>, enacl:box_NONCEBYTES()()),
    box_afternm(B, Nonce, R, 10*1000),
    ok.

box_beforenm(_PK, _SK, 0) -> ok;
box_beforenm(PK, SK, N) ->
    enacl:box_beforenm(PK, SK),
    box_beforenm(PK, SK, N-1).

box_afternm(_Msg, _Nonce, _Key, 0) -> ok;
box_afternm(Msg, Nonce, Key, N) ->
    enacl:box_afternm(Msg, Nonce, Key),
    box_afternm(Msg, Nonce, Key, N-1).

sign_keypair() ->
    sign_keypair(10*1000).

sign_keypair(0) -> ok;
sign_keypair(N) ->
    enacl:sign_keypair(),
    #{ public := PK, secret := SK} = enacl:crypto_sign_ed25519_keypair(),
    enacl:crypto_sign_ed25519_public_to_curve25519(PK),
    enacl:crypto_sign_ed25519_secret_to_curve25519(SK),
    sign_keypair(N-1).

sign() ->
    Msg = binary:copy(<<0>>, 1024),
    #{ secret := SK } = enacl:sign_keypair(),
    sign(Msg, SK, 10*1000).

sign(_Msg, _SK, 0) -> ok;
sign(Msg, SK, N) ->
    enacl:sign(Msg, SK),
    enacl:sign_detached(Msg, SK),
    sign(Msg, SK, N-1).

secretbox() ->
    Msg = binary:copy(<<0>>, 8192),
    Nonce = binary:copy(<<0>>, enacl:secretbox_NONCEBYTES()()),
    Key = binary:copy(<<0>>, enacl:secretbox_KEYBYTES()),
    secretbox(Msg, Nonce, Key, 10*1000).

secretbox(_Msg, _Nonce, _Key, 0) -> ok;
secretbox(Msg, Nonce, Key, N) ->
    enacl:secretbox(Msg, Nonce, Key),
    secretbox(Msg, Nonce, Key, N-1).


stream() ->
    stream(16384, binary:copy(<<0>>, enacl:stream_NONCEBYTES()), binary:copy(<<0>>, enacl:stream_KEYBYTES()), 10*1000).

stream(_L, _Nonce, _K, 0) -> ok;
stream(L, Nonce, K, N) ->
    enacl:stream(L, Nonce, K),
    stream(L, Nonce, K, N-1).

auth() ->
    Msg = binary:copy(<<0>>, 4096),
    Key = binary:copy(<<0>>, enacl:auth_KEYBYTES()),
    auth(Msg, Key, 10*1000).

auth(_Msg, _Key, 0) -> ok;
auth(Msg, Key, N) ->
    enacl:auth(Msg, Key),
    auth(Msg, Key, N-1).

onetime_auth() ->
    Msg = binary:copy(<<0>>, 16384),
    Key = binary:copy(<<0>>, enacl:onetime_auth_KEYBYTES()),
    onetime_auth(Msg, Key, 10*1000).

onetime_auth(_Msg, _Key, 0) -> ok;
onetime_auth(Msg, Key, N) ->
    enacl:onetime_auth(Msg, Key),
    onetime_auth(Msg, Key, N-1).

scalarmult() ->
    Secret = binary:copy(<<0>>, 32),
    BasePoint = binary:copy(<<1>>, 32),
    scalarmult(Secret, BasePoint, 10*1000).

scalarmult(_S, _B, 0) -> ok;
scalarmult(S, B, N) ->
    enacl:curve25519_scalarmult(S, B),
    scalarmult(S, B, N-1).



================================================
FILE: benchmark.sh
================================================
#!/bin/bash

case $1 in
	"on" )
		for i in /sys/devices/system/cpu/cpu[0-7] ; do
			echo performance > $i/cpufreq/scaling_governor
		done;;
	"off" )
		for i in /sys/devices/system/cpu/cpu[0-7] ; do
			echo powersave > $i/cpufreq/scaling_governor
		done;;
	*)
		echo "Usage: $0 on|off";;
esac



================================================
FILE: c_src/aead.c
================================================
#include <sodium.h>

#include <erl_nif.h>

#include "aead.h"
#include "enacl.h"

/*
 * AEAD ChaCha20 Poly1305
 */
ERL_NIF_TERM
enacl_crypto_aead_chacha20poly1305_ietf_KEYBYTES(ErlNifEnv *env, int argc,
                                                 ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_aead_chacha20poly1305_ietf_KEYBYTES);
}

ERL_NIF_TERM
enacl_crypto_aead_chacha20poly1305_ietf_NPUBBYTES(ErlNifEnv *env, int argc,
                                                  ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_aead_chacha20poly1305_ietf_NPUBBYTES);
}

ERL_NIF_TERM
enacl_crypto_aead_chacha20poly1305_ietf_ABYTES(ErlNifEnv *env, int argc,
                                               ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_aead_chacha20poly1305_ietf_ABYTES);
}

ERL_NIF_TERM
enacl_crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX(
    ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env,
                         crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX);
}

ERL_NIF_TERM
enacl_crypto_aead_chacha20poly1305_ietf_encrypt(ErlNifEnv *env, int argc,
                                                ERL_NIF_TERM const argv[]) {
  ERL_NIF_TERM ret;
  ErlNifBinary key, nonce, ad, message, ciphertext;

  if (argc != 4)
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[0], &message))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[1], &ad))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[2], &nonce))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[3], &key))
    goto bad_arg;
  if (key.size != crypto_aead_chacha20poly1305_ietf_KEYBYTES)
    goto bad_arg;
  if (nonce.size != crypto_aead_chacha20poly1305_ietf_NPUBBYTES)
    goto bad_arg;

  if (!enif_alloc_binary(message.size +
                             crypto_aead_chacha20poly1305_ietf_ABYTES,
                         &ciphertext)) {
    goto err;
  }

  crypto_aead_chacha20poly1305_ietf_encrypt(ciphertext.data, NULL, message.data,
                                            message.size, ad.data, ad.size,
                                            NULL, nonce.data, key.data);

  ret = enif_make_binary(env, &ciphertext);
  goto done;

bad_arg:
  return enif_make_badarg(env);
err:
  ret = enacl_internal_error(env);
done:
  return ret;
}

ERL_NIF_TERM
enacl_crypto_aead_chacha20poly1305_ietf_decrypt(ErlNifEnv *env, int argc,
                                                ERL_NIF_TERM const argv[]) {
  ERL_NIF_TERM ret;
  ErlNifBinary key, nonce, ad, message, ciphertext;

  if (argc != 4)
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[0], &ciphertext))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[1], &ad))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[2], &nonce))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[3], &key))
    goto bad_arg;

  if (ciphertext.size < crypto_aead_chacha20poly1305_ietf_ABYTES)
    goto bad_arg;
  if (key.size != crypto_aead_chacha20poly1305_ietf_KEYBYTES)
    goto bad_arg;
  if (nonce.size != crypto_aead_chacha20poly1305_ietf_NPUBBYTES)
    goto bad_arg;

  if (!enif_alloc_binary(ciphertext.size -
                             crypto_aead_chacha20poly1305_ietf_ABYTES,
                         &message)) {
    return enacl_internal_error(env);
  }

  if (crypto_aead_chacha20poly1305_ietf_decrypt(
          message.data, NULL, NULL, ciphertext.data, ciphertext.size, ad.data,
          ad.size, nonce.data, key.data) != 0) {
    ret = enacl_error_tuple(env, "failed_verification");
    goto release;
  }

  ret = enif_make_binary(env, &message);
  goto done;
bad_arg:
  return enif_make_badarg(env);
release:
  enif_release_binary(&message);
done:
  return ret;
}

/*
 * AEAD XChaCha20 Poly1305
 */
ERL_NIF_TERM
enacl_crypto_aead_xchacha20poly1305_ietf_KEYBYTES(ErlNifEnv *env, int argc,
                                                  ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_aead_xchacha20poly1305_ietf_KEYBYTES);
}

ERL_NIF_TERM
enacl_crypto_aead_xchacha20poly1305_ietf_NPUBBYTES(ErlNifEnv *env, int argc,
                                                   ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
}

ERL_NIF_TERM
enacl_crypto_aead_xchacha20poly1305_ietf_ABYTES(ErlNifEnv *env, int argc,
                                                ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_aead_xchacha20poly1305_ietf_ABYTES);
}

ERL_NIF_TERM
enacl_crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX(
    ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env,
                         crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX);
}

ERL_NIF_TERM
enacl_crypto_aead_xchacha20poly1305_ietf_encrypt(ErlNifEnv *env, int argc,
                                                 ERL_NIF_TERM const argv[]) {
  ErlNifBinary key, nonce, ad, message, ciphertext;
  ERL_NIF_TERM ret;

  if (argc != 4)
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[0], &message))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[1], &ad))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[2], &nonce))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[3], &key))
    goto bad_arg;

  if (key.size != crypto_aead_xchacha20poly1305_ietf_KEYBYTES)
    goto bad_arg;
  if (nonce.size != crypto_aead_xchacha20poly1305_ietf_NPUBBYTES)
    goto bad_arg;

  if (!enif_alloc_binary(message.size +
                             crypto_aead_xchacha20poly1305_ietf_ABYTES,
                         &ciphertext)) {
    goto err;
  }

  crypto_aead_xchacha20poly1305_ietf_encrypt(
      ciphertext.data, NULL, message.data, message.size, ad.data, ad.size, NULL,
      nonce.data, key.data);

  ret = enif_make_binary(env, &ciphertext);
  goto done;

bad_arg:
  return enif_make_badarg(env);
err:
  ret = enacl_internal_error(env);
done:
  return ret;
}

ERL_NIF_TERM
enacl_crypto_aead_xchacha20poly1305_ietf_decrypt(ErlNifEnv *env, int argc,
                                                 ERL_NIF_TERM const argv[]) {
  ErlNifBinary key, nonce, ad, message, ciphertext;
  ERL_NIF_TERM ret;

  if (argc != 4)
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[0], &ciphertext))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[1], &ad))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[2], &nonce))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[3], &key))
    goto bad_arg;

  if (ciphertext.size < crypto_aead_xchacha20poly1305_ietf_ABYTES)
    goto bad_arg;
  if (key.size != crypto_aead_xchacha20poly1305_ietf_KEYBYTES)
    goto bad_arg;
  if (nonce.size != crypto_aead_xchacha20poly1305_ietf_NPUBBYTES)
    goto bad_arg;

  if (!enif_alloc_binary(ciphertext.size -
                             crypto_aead_xchacha20poly1305_ietf_ABYTES,
                         &message)) {
    return enacl_internal_error(env);
  }

  if (crypto_aead_xchacha20poly1305_ietf_decrypt(
          message.data, NULL, NULL, ciphertext.data, ciphertext.size, ad.data,
          ad.size, nonce.data, key.data) != 0) {
    ret = enacl_error_tuple(env, "failed_verification");
    goto release;
  }

  ret = enif_make_binary(env, &message);
  goto done;

bad_arg:
  return enif_make_badarg(env);
release:
  enif_release_binary(&message);
done:
  return ret;
}


================================================
FILE: c_src/aead.h
================================================
#ifndef ENACL_AEAD_H
#define ENACL_AEAD_H

#include <erl_nif.h>

/* AEAD ChaCha20 Poly1305 */
ERL_NIF_TERM
enacl_crypto_aead_chacha20poly1305_ietf_KEYBYTES(ErlNifEnv *env, int argc,
                                                 ERL_NIF_TERM const argv[]);
ERL_NIF_TERM
enacl_crypto_aead_chacha20poly1305_ietf_NPUBBYTES(ErlNifEnv *env, int argc,
                                                  ERL_NIF_TERM const argv[]);
ERL_NIF_TERM
enacl_crypto_aead_chacha20poly1305_ietf_ABYTES(ErlNifEnv *env, int argc,
                                               ERL_NIF_TERM const argv[]);
ERL_NIF_TERM
enacl_crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX(
    ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]);
ERL_NIF_TERM
enacl_crypto_aead_chacha20poly1305_ietf_encrypt(ErlNifEnv *env, int argc,
                                                ERL_NIF_TERM const argv[]);
ERL_NIF_TERM
enacl_crypto_aead_chacha20poly1305_ietf_decrypt(ErlNifEnv *env, int argc,
                                                ERL_NIF_TERM const argv[]);

/* AEAD XChaCha20 Poly1305 */
ERL_NIF_TERM
enacl_crypto_aead_xchacha20poly1305_ietf_KEYBYTES(ErlNifEnv *env, int argc,
                                                  ERL_NIF_TERM const argv[]);
ERL_NIF_TERM
enacl_crypto_aead_xchacha20poly1305_ietf_NPUBBYTES(ErlNifEnv *env, int argc,
                                                   ERL_NIF_TERM const argv[]);
ERL_NIF_TERM
enacl_crypto_aead_xchacha20poly1305_ietf_ABYTES(ErlNifEnv *env, int argc,
                                                ERL_NIF_TERM const argv[]);
ERL_NIF_TERM
enacl_crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX(
    ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]);
ERL_NIF_TERM
enacl_crypto_aead_xchacha20poly1305_ietf_encrypt(ErlNifEnv *env, int argc,
                                                 ERL_NIF_TERM const argv[]);
ERL_NIF_TERM
enacl_crypto_aead_xchacha20poly1305_ietf_decrypt(ErlNifEnv *env, int argc,
                                                 ERL_NIF_TERM const argv[]);

#endif


================================================
FILE: c_src/enacl.c
================================================
#include <erl_nif.h>

#include "enacl.h"

ERL_NIF_TERM enacl_error_tuple(ErlNifEnv *env, char *error_atom) {
  return enif_make_tuple2(env, enif_make_atom(env, "error"),
                          enif_make_atom(env, error_atom));
}

ERL_NIF_TERM enacl_internal_error(ErlNifEnv *env) {
  return enif_raise_exception(env, enif_make_atom(env, "enacl_internal_error"));
}

ERL_NIF_TERM enacl_error_finalized(ErlNifEnv *env) {
  return enif_raise_exception(env, enif_make_atom(env, "enacl_finalized"));
}

================================================
FILE: c_src/enacl.h
================================================
#ifndef ENACL_H
#define ENACL_H

#include <erl_nif.h>

#define ATOM_OK "ok"
#define ATOM_ERROR "error"
#define ATOM_TRUE "true"
#define ATOM_FALSE "false"

ERL_NIF_TERM enacl_error_tuple(ErlNifEnv *, char *);
ERL_NIF_TERM enacl_error_finalized(ErlNifEnv *);
ERL_NIF_TERM enacl_internal_error(ErlNifEnv *);

#endif


================================================
FILE: c_src/enacl_ext.c
================================================
#include <sodium.h>

#include <erl_nif.h>

#include "enacl.h"
#include "enacl_ext.h"

static void uint64_pack(unsigned char *y, ErlNifUInt64 x) {
  *y++ = x;
  x >>= 8;
  *y++ = x;
  x >>= 8;
  *y++ = x;
  x >>= 8;
  *y++ = x;
  x >>= 8;
  *y++ = x;
  x >>= 8;
  *y++ = x;
  x >>= 8;
  *y++ = x;
  x >>= 8;
  *y++ = x;
}

static ErlNifUInt64 uint64_unpack(const unsigned char *x) {
  ErlNifUInt64 result;

  result = x[7];
  result <<= 8;
  result |= x[6];
  result <<= 8;
  result |= x[5];
  result <<= 8;
  result |= x[4];
  result <<= 8;
  result |= x[3];
  result <<= 8;
  result |= x[2];
  result <<= 8;
  result |= x[1];
  result <<= 8;
  result |= x[0];
  return result;
}

static int crypto_block(unsigned char *out, const unsigned char *in,
                        const unsigned char *k) {
  ErlNifUInt64 v0 = uint64_unpack(in + 0);
  ErlNifUInt64 v1 = uint64_unpack(in + 8);
  ErlNifUInt64 k0 = uint64_unpack(k + 0);
  ErlNifUInt64 k1 = uint64_unpack(k + 8);
  ErlNifUInt64 k2 = uint64_unpack(k + 16);
  ErlNifUInt64 k3 = uint64_unpack(k + 24);
  ErlNifUInt64 sum = 0;
  ErlNifUInt64 delta = 0x9e3779b97f4a7c15;
  int i;
  for (i = 0; i < 32; ++i) {
    sum += delta;
    v0 += ((v1 << 7) + k0) ^ (v1 + sum) ^ ((v1 >> 12) + k1);
    v1 += ((v0 << 16) + k2) ^ (v0 + sum) ^ ((v0 >> 8) + k3);
  }
  uint64_pack(out + 0, v0);
  uint64_pack(out + 8, v1);

  return 0;
}

ERL_NIF_TERM enif_scramble_block_16(ErlNifEnv *env, int argc,
                                    ERL_NIF_TERM const argv[]) {
  ErlNifBinary in, out, key;

  if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &in)) ||
      (!enif_inspect_binary(env, argv[1], &key)) || (in.size != 16) ||
      (key.size != 32)) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(in.size, &out)) {
    return enacl_internal_error(env);
  }

  crypto_block(out.data, in.data, key.data);

  return enif_make_binary(env, &out);
}

================================================
FILE: c_src/enacl_ext.h
================================================
#ifndef ENACL_EXT_H
#define ENACL_EXT_H

#include <erl_nif.h>

ERL_NIF_TERM enif_scramble_block_16(ErlNifEnv *env, int argc,
                                    ERL_NIF_TERM const argv[]);

#endif


================================================
FILE: c_src/enacl_nif.c
================================================
#include <sodium.h>
#include <string.h>

#include <erl_nif.h>

#include "aead.h"
#include "enacl.h"
#include "enacl_ext.h"
#include "generichash.h"
#include "hash.h"
#include "kdf.h"
#include "kx.h"
#include "public.h"
#include "pwhash.h"
#include "randombytes.h"
#include "secret.h"
#include "secretstream.h"
#include "sign.h"

#ifdef ERL_NIF_DIRTY_JOB_CPU_BOUND
#define erl_nif_dirty_job_cpu_bound_macro(a, b, c)                             \
  { a, b, c, ERL_NIF_DIRTY_JOB_CPU_BOUND }
#else
#define erl_nif_dirty_job_cpu_bound_macro(a, b, c)                             \
  { a, b, c }
#endif

/* Initialization */
static int enacl_crypto_load(ErlNifEnv *env, void **priv_data,
                             ERL_NIF_TERM load_info) {
  // Create a new resource type for crypto_generichash_state
  if (!enacl_init_generic_hash_ctx(env)) {
    return -1;
  }

  if (!enacl_init_sign_ctx(env)) {
    return -1;
  }

  if (!enacl_init_secretstream_ctx(env)) {
    return -1;
  }

  if (sodium_init() == -1) {
    return -1;
  }

  return 0;
}

static int enacl_crypto_upgrade(ErlNifEnv* env, void **priv_data,
                                void **old_priv_data,
                                ERL_NIF_TERM load_info) {
    return 0;
}

static int enacl_crypto_unload(ErlNifEnv* env, void **priv_data,
                                ERL_NIF_TERM load_info) {
    return 0;
}

/* GENERAL ROUTINES
 *
 * These don't generally fit somewhere else nicely, so we keep them in the main
 * file
 *
 */
static ERL_NIF_TERM enacl_crypto_verify_16(ErlNifEnv *env, int argc,
                                           ERL_NIF_TERM const argv[]) {
  ErlNifBinary x, y;

  if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &x)) ||
      (!enif_inspect_binary(env, argv[1], &y))) {
    return enif_make_badarg(env);
  }

  if (x.size != 16 || y.size != 16) {
    return enif_make_badarg(env);
  }

  if (0 == crypto_verify_16(x.data, y.data)) {
    return enif_make_atom(env, "true");
  } else {
    return enif_make_atom(env, "false");
  }
}

static ERL_NIF_TERM enacl_crypto_verify_32(ErlNifEnv *env, int argc,
                                           ERL_NIF_TERM const argv[]) {
  ErlNifBinary x, y;

  if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &x)) ||
      (!enif_inspect_binary(env, argv[1], &y))) {
    return enif_make_badarg(env);
  }

  if (x.size != 32 || y.size != 32) {
    return enif_make_badarg(env);
  }

  if (0 == crypto_verify_32(x.data, y.data)) {
    return enif_make_atom(env, "true");
  } else {
    return enif_make_atom(env, "false");
  }
}

/* This is very unsafe. It will not affect things that have been
  binary_copy()'ed Use this for destroying key material from ram but nothing
  more. Be careful! */
static ERL_NIF_TERM enif_sodium_memzero(ErlNifEnv *env, int argc,
                                        ERL_NIF_TERM const argv[]) {
  ErlNifBinary x;

  if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &x))) {
    return enif_make_badarg(env);
  }

  sodium_memzero(x.data, x.size);

  return enif_make_atom(env, "ok");
}

/* Curve 25519 */
static ERL_NIF_TERM
enacl_crypto_curve25519_scalarmult(ErlNifEnv *env, int argc,
                                   ERL_NIF_TERM const argv[]) {
  ERL_NIF_TERM result;
  ErlNifBinary secret, basepoint, output;
  uint8_t bp[crypto_scalarmult_curve25519_BYTES];

  if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &secret)) ||
      (!enif_inspect_binary(env, argv[1], &basepoint)) ||
      (secret.size != crypto_scalarmult_curve25519_BYTES) ||
      (basepoint.size != crypto_scalarmult_curve25519_BYTES)) {
    return enif_make_badarg(env);
  }

  memcpy(bp, basepoint.data, crypto_scalarmult_curve25519_BYTES);

  /* Clear the high-bit. Better safe than sorry. */
  bp[crypto_scalarmult_curve25519_BYTES - 1] &= 0x7f;

  do {
    if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &output)) {
      result = enacl_internal_error(env);
      continue;
    }

    if (crypto_scalarmult_curve25519(output.data, secret.data, bp) != 0) {
      enif_release_binary(&output);
      result = enacl_error_tuple(env, "scalarmult_curve25519_failed");
      continue;
    }

    result = enif_make_binary(env, &output);
  } while (0);

  sodium_memzero(bp, crypto_scalarmult_curve25519_BYTES);

  return result;
}

static ERL_NIF_TERM
enacl_crypto_curve25519_scalarmult_base(ErlNifEnv *env, int argc,
                                        ERL_NIF_TERM const argv[]) {
  ERL_NIF_TERM result;
  ErlNifBinary secret, output;

  if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &secret)) ||
      (secret.size != crypto_scalarmult_curve25519_BYTES)) {
    return enif_make_badarg(env);
  }

  do {
    if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &output)) {
      result = enacl_internal_error(env);
      continue;
    }

    if (crypto_scalarmult_curve25519_base(output.data, secret.data) != 0) {
      enif_release_binary(&output);
      result = enacl_error_tuple(env, "scalarmult_curve25519_base_failed");
      continue;
    }

    result = enif_make_binary(env, &output);
  } while (0);

  return result;
}

/* Tie the knot to the Erlang world */
static ErlNifFunc nif_funcs[] = {
    {"crypto_box_NONCEBYTES", 0, enacl_crypto_box_NONCEBYTES},
    {"crypto_box_ZEROBYTES", 0, enacl_crypto_box_ZEROBYTES},
    {"crypto_box_BOXZEROBYTES", 0, enacl_crypto_box_BOXZEROBYTES},
    {"crypto_box_PUBLICKEYBYTES", 0, enacl_crypto_box_PUBLICKEYBYTES},
    {"crypto_box_SECRETKEYBYTES", 0, enacl_crypto_box_SECRETKEYBYTES},
    {"crypto_box_BEFORENMBYTES", 0, enacl_crypto_box_BEFORENMBYTES},

    erl_nif_dirty_job_cpu_bound_macro("crypto_box_keypair", 0,
                                      enacl_crypto_box_keypair),

    erl_nif_dirty_job_cpu_bound_macro("crypto_box", 4, enacl_crypto_box),
    erl_nif_dirty_job_cpu_bound_macro("crypto_box_open", 4,
                                      enacl_crypto_box_open),

    {"crypto_box_beforenm", 2, enacl_crypto_box_beforenm},
    {"crypto_box_afternm_b", 3, enacl_crypto_box_afternm},
    erl_nif_dirty_job_cpu_bound_macro("crypto_box_afternm", 3,
                                      enacl_crypto_box_afternm),
    {"crypto_box_open_afternm_b", 3, enacl_crypto_box_open_afternm},
    erl_nif_dirty_job_cpu_bound_macro("crypto_box_open_afternm", 3,
                                      enacl_crypto_box_open_afternm),

    {"crypto_sign_PUBLICKEYBYTES", 0, enacl_crypto_sign_PUBLICKEYBYTES},
    {"crypto_sign_SECRETKEYBYTES", 0, enacl_crypto_sign_SECRETKEYBYTES},
    erl_nif_dirty_job_cpu_bound_macro("crypto_sign_keypair", 0,
                                      enacl_crypto_sign_keypair),
    erl_nif_dirty_job_cpu_bound_macro("crypto_sign_seed_keypair", 1,
                                      enacl_crypto_sign_seed_keypair),

    erl_nif_dirty_job_cpu_bound_macro("crypto_sign", 2, enacl_crypto_sign),
    erl_nif_dirty_job_cpu_bound_macro("crypto_sign_open", 2,
                                      enacl_crypto_sign_open),

    erl_nif_dirty_job_cpu_bound_macro("crypto_sign_detached", 2,
                                      enacl_crypto_sign_detached),
    erl_nif_dirty_job_cpu_bound_macro("crypto_sign_verify_detached", 3,
                                      enacl_crypto_sign_verify_detached),
    {"crypto_sign_init", 0, enacl_crypto_sign_init},
    erl_nif_dirty_job_cpu_bound_macro("crypto_sign_update", 2,
                                      enacl_crypto_sign_update),
    erl_nif_dirty_job_cpu_bound_macro("crypto_sign_final_create", 2,
                                      enacl_crypto_sign_final_create),
    erl_nif_dirty_job_cpu_bound_macro("crypto_sign_final_verify", 3,
                                      enacl_crypto_sign_final_verify),

    {"crypto_sign_ed25519_sk_to_pk", 1, enacl_crypto_sign_ed25519_sk_to_pk},

    {"crypto_box_SEALBYTES", 0, enacl_crypto_box_SEALBYTES},

    erl_nif_dirty_job_cpu_bound_macro("crypto_box_seal", 2,
                                      enacl_crypto_box_seal),
    erl_nif_dirty_job_cpu_bound_macro("crypto_box_seal_open", 3,
                                      enacl_crypto_box_seal_open),

    {"crypto_secretbox_NONCEBYTES", 0, enacl_crypto_secretbox_NONCEBYTES},
    {"crypto_secretbox_ZEROBYTES", 0, enacl_crypto_secretbox_ZEROBYTES},
    {"crypto_secretbox_BOXZEROBYTES", 0, enacl_crypto_secretbox_BOXZEROBYTES},
    {"crypto_secretbox_KEYBYTES", 0, enacl_crypto_secretbox_KEYBYTES},
    {"crypto_secretbox_b", 3, enacl_crypto_secretbox},
    erl_nif_dirty_job_cpu_bound_macro("crypto_secretbox", 3,
                                      enacl_crypto_secretbox),
    {"crypto_secretbox_open_b", 3, enacl_crypto_secretbox_open},
    erl_nif_dirty_job_cpu_bound_macro("crypto_secretbox_open", 3,
                                      enacl_crypto_secretbox_open),

    {"crypto_stream_chacha20_KEYBYTES", 0,
     enacl_crypto_stream_chacha20_KEYBYTES},
    {"crypto_stream_chacha20_NONCEBYTES", 0,
     enacl_crypto_stream_chacha20_NONCEBYTES},
    {"crypto_stream_chacha20_b", 3, enacl_crypto_stream_chacha20},
    erl_nif_dirty_job_cpu_bound_macro("crypto_stream_chacha20", 3,
                                      enacl_crypto_stream_chacha20),
    {"crypto_stream_chacha20_xor_b", 3, enacl_crypto_stream_chacha20_xor},
    erl_nif_dirty_job_cpu_bound_macro("crypto_stream_chacha20_xor", 3,
                                      enacl_crypto_stream_chacha20_xor),

    {"crypto_stream_KEYBYTES", 0, enacl_crypto_stream_KEYBYTES},
    {"crypto_stream_NONCEBYTES", 0, enacl_crypto_stream_NONCEBYTES},
    {"crypto_stream_b", 3, enacl_crypto_stream},
    erl_nif_dirty_job_cpu_bound_macro("crypto_stream", 3, enacl_crypto_stream),
    {"crypto_stream_xor_b", 3, enacl_crypto_stream_xor},
    erl_nif_dirty_job_cpu_bound_macro("crypto_stream_xor", 3,
                                      enacl_crypto_stream_xor),

    {"crypto_auth_BYTES", 0, enacl_crypto_auth_BYTES},
    {"crypto_auth_KEYBYTES", 0, enacl_crypto_auth_KEYBYTES},
    {"crypto_auth_b", 2, enacl_crypto_auth},
    erl_nif_dirty_job_cpu_bound_macro("crypto_auth", 2, enacl_crypto_auth),
    {"crypto_auth_verify_b", 3, enacl_crypto_auth_verify},
    erl_nif_dirty_job_cpu_bound_macro("crypto_auth_verify", 3,
                                      enacl_crypto_auth_verify),

    {"crypto_shorthash_BYTES", 0, enacl_crypto_shorthash_BYTES},
    {"crypto_shorthash_KEYBYTES", 0, enacl_crypto_shorthash_KEYBYTES},
    {"crypto_shorthash", 2, enacl_crypto_shorthash},

    {"crypto_onetimeauth_BYTES", 0, enacl_crypto_onetimeauth_BYTES},
    {"crypto_onetimeauth_KEYBYTES", 0, enacl_crypto_onetimeauth_KEYBYTES},
    {"crypto_onetimeauth_b", 2, enacl_crypto_onetimeauth},
    erl_nif_dirty_job_cpu_bound_macro("crypto_onetimeauth", 2,
                                      enacl_crypto_onetimeauth),
    {"crypto_onetimeauth_verify_b", 3, enacl_crypto_onetimeauth_verify},
    erl_nif_dirty_job_cpu_bound_macro("crypto_onetimeauth_verify", 3,
                                      enacl_crypto_onetimeauth_verify),

    {"crypto_hash_b", 1, enacl_crypto_hash},
    erl_nif_dirty_job_cpu_bound_macro("crypto_hash", 1, enacl_crypto_hash),
    {"crypto_verify_16", 2, enacl_crypto_verify_16},
    {"crypto_verify_32", 2, enacl_crypto_verify_32},
    {"sodium_memzero", 1, enif_sodium_memzero},

    {"crypto_pwhash_SALTBYTES", 0, enacl_crypto_pwhash_SALTBYTES},
    erl_nif_dirty_job_cpu_bound_macro("crypto_pwhash", 5, enacl_crypto_pwhash),
    erl_nif_dirty_job_cpu_bound_macro("crypto_pwhash_str", 3,
                                      enacl_crypto_pwhash_str),
    erl_nif_dirty_job_cpu_bound_macro("crypto_pwhash_str_verify", 2,
                                      enacl_crypto_pwhash_str_verify),

    {"crypto_kdf_KEYBYTES", 0, enacl_crypto_kdf_KEYBYTES},
    {"crypto_kdf_CONTEXTBYTES", 0, enacl_crypto_kdf_CONTEXTBYTES},
    erl_nif_dirty_job_cpu_bound_macro("crypto_kdf_derive_from_key", 3,
                                      enacl_crypto_kdf_derive_from_key),

    erl_nif_dirty_job_cpu_bound_macro("crypto_curve25519_scalarmult", 2,
                                      enacl_crypto_curve25519_scalarmult),
    erl_nif_dirty_job_cpu_bound_macro("crypto_curve25519_scalarmult_base", 1,
                                      enacl_crypto_curve25519_scalarmult_base),

    erl_nif_dirty_job_cpu_bound_macro("crypto_sign_ed25519_keypair", 0,
                                      enacl_crypto_sign_ed25519_keypair),
    {"crypto_sign_ed25519_public_to_curve25519", 1,
     enacl_crypto_sign_ed25519_public_to_curve25519},
    {"crypto_sign_ed25519_secret_to_curve25519", 1,
     enacl_crypto_sign_ed25519_secret_to_curve25519},
    {"crypto_sign_ed25519_PUBLICKEYBYTES", 0,
     enacl_crypto_sign_ed25519_PUBLICKEYBYTES},
    {"crypto_sign_ed25519_SECRETKEYBYTES", 0,
     enacl_crypto_sign_ed25519_SECRETKEYBYTES},

    // Linux might block here if early in the boot sequence, so get it off the
    // main scheduler. Otherwise, it it would probably be fine to run on the
    // main scheduler. This plays it safe, albeit with a performance hit.
    //
    // However: you shouldn't use a CSPRNG unless you need one. So it is
    // probably fine to do the dirty-scheduler dance. Using the random
    // material should dwarf the extraction of random material.
    erl_nif_dirty_job_cpu_bound_macro("randombytes", 1, enif_randombytes),
    erl_nif_dirty_job_cpu_bound_macro("randombytes_uint32", 0,
                                      enif_randombytes_uint32),
    erl_nif_dirty_job_cpu_bound_macro("randombytes_uniform", 1,
                                      enif_randombytes_uniform),

    erl_nif_dirty_job_cpu_bound_macro("crypto_kx_keypair", 0,
                                      enacl_crypto_kx_keypair),
    erl_nif_dirty_job_cpu_bound_macro("crypto_kx_client_session_keys", 3,
                                      enacl_crypto_kx_client_session_keys),
    erl_nif_dirty_job_cpu_bound_macro("crypto_kx_server_session_keys", 3,
                                      enacl_crypto_kx_server_session_keys),
    {"crypto_kx_PUBLICKEYBYTES", 0, enacl_crypto_kx_PUBLICKEYBYTES},
    {"crypto_kx_SECRETKEYBYTES", 0, enacl_crypto_kx_SECRETKEYBYTES},
    {"crypto_kx_SESSIONKEYBYTES", 0, enacl_crypto_kx_SESSIONKEYBYTES},

    {"scramble_block_16", 2, enif_scramble_block_16},

    {"crypto_aead_chacha20poly1305_ietf_KEYBYTES", 0,
     enacl_crypto_aead_chacha20poly1305_ietf_KEYBYTES},
    {"crypto_aead_chacha20poly1305_ietf_NPUBBYTES", 0,
     enacl_crypto_aead_chacha20poly1305_ietf_NPUBBYTES},
    {"crypto_aead_chacha20poly1305_ietf_ABYTES", 0,
     enacl_crypto_aead_chacha20poly1305_ietf_ABYTES},
    {"crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX", 0,
     enacl_crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX},
    erl_nif_dirty_job_cpu_bound_macro(
        "crypto_aead_chacha20poly1305_ietf_encrypt", 4,
        enacl_crypto_aead_chacha20poly1305_ietf_encrypt),
    erl_nif_dirty_job_cpu_bound_macro(
        "crypto_aead_chacha20poly1305_ietf_decrypt", 4,
        enacl_crypto_aead_chacha20poly1305_ietf_decrypt),

    {"crypto_aead_xchacha20poly1305_ietf_KEYBYTES", 0,
     enacl_crypto_aead_xchacha20poly1305_ietf_KEYBYTES},
    {"crypto_aead_xchacha20poly1305_ietf_NPUBBYTES", 0,
     enacl_crypto_aead_xchacha20poly1305_ietf_NPUBBYTES},
    {"crypto_aead_xchacha20poly1305_ietf_ABYTES", 0,
     enacl_crypto_aead_xchacha20poly1305_ietf_ABYTES},
    {"crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX", 0,
     enacl_crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX},
    erl_nif_dirty_job_cpu_bound_macro(
        "crypto_aead_xchacha20poly1305_ietf_encrypt", 4,
        enacl_crypto_aead_xchacha20poly1305_ietf_encrypt),
    erl_nif_dirty_job_cpu_bound_macro(
        "crypto_aead_xchacha20poly1305_ietf_decrypt", 4,
        enacl_crypto_aead_xchacha20poly1305_ietf_decrypt),

    {"crypto_generichash_BYTES", 0, enacl_crypto_generichash_BYTES},
    {"crypto_generichash_BYTES_MIN", 0, enacl_crypto_generichash_BYTES_MIN},
    {"crypto_generichash_BYTES_MAX", 0, enacl_crypto_generichash_BYTES_MAX},
    {"crypto_generichash_KEYBYTES", 0, enacl_crypto_generichash_KEYBYTES},
    {"crypto_generichash_KEYBYTES_MIN", 0,
     enacl_crypto_generichash_KEYBYTES_MIN},
    {"crypto_generichash_KEYBYTES_MAX", 0,
     enacl_crypto_generichash_KEYBYTES_MAX},
    {"crypto_generichash", 3, enacl_crypto_generichash},
    {"crypto_generichash_init", 2, enacl_crypto_generichash_init},
    erl_nif_dirty_job_cpu_bound_macro("crypto_generichash_update", 2,
                                      enacl_crypto_generichash_update),
    erl_nif_dirty_job_cpu_bound_macro("crypto_generichash_final", 1,
                                      enacl_crypto_generichash_final),

    {"crypto_secretstream_xchacha20poly1305_ABYTES", 0,
     enacl_crypto_secretstream_xchacha20poly1305_ABYTES},
    {"crypto_secretstream_xchacha20poly1305_HEADERBYTES", 0,
     enacl_crypto_secretstream_xchacha20poly1305_HEADERBYTES},
    {"crypto_secretstream_xchacha20poly1305_KEYBYTES", 0,
     enacl_crypto_secretstream_xchacha20poly1305_KEYBYTES},
    {"crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX", 0,
     enacl_crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX},
    {"crypto_secretstream_xchacha20poly1305_TAG_MESSAGE", 0,
     enacl_crypto_secretstream_xchacha20poly1305_TAG_MESSAGE},
    {"crypto_secretstream_xchacha20poly1305_TAG_PUSH", 0,
     enacl_crypto_secretstream_xchacha20poly1305_TAG_PUSH},
    {"crypto_secretstream_xchacha20poly1305_TAG_REKEY", 0,
     enacl_crypto_secretstream_xchacha20poly1305_TAG_REKEY},
    {"crypto_secretstream_xchacha20poly1305_TAG_FINAL", 0,
     enacl_crypto_secretstream_xchacha20poly1305_TAG_FINAL},
    {"crypto_secretstream_xchacha20poly1305_keygen", 0,
     enacl_crypto_secretstream_xchacha20poly1305_keygen},
    {"crypto_secretstream_xchacha20poly1305_init_push", 1,
     enacl_crypto_secretstream_xchacha20poly1305_init_push},
    {"crypto_secretstream_xchacha20poly1305_init_pull", 2,
     enacl_crypto_secretstream_xchacha20poly1305_init_pull},
    {"crypto_secretstream_xchacha20poly1305_rekey", 1,
     enacl_crypto_secretstream_xchacha20poly1305_rekey},
    erl_nif_dirty_job_cpu_bound_macro(
        "crypto_secretstream_xchacha20poly1305_push", 4,
        enacl_crypto_secretstream_xchacha20poly1305_push),
    erl_nif_dirty_job_cpu_bound_macro(
        "crypto_secretstream_xchacha20poly1305_pull", 3,
        enacl_crypto_secretstream_xchacha20poly1305_pull)};

ERL_NIF_INIT(enacl_nif, nif_funcs, enacl_crypto_load, NULL, enacl_crypto_upgrade, enacl_crypto_unload);


================================================
FILE: c_src/generichash.c
================================================
#include <sodium.h>

#include <erl_nif.h>

#include "enacl.h"
#include "generichash.h"

typedef struct enacl_generichash_ctx {
  ErlNifMutex *mtx;
  crypto_generichash_state *ctx; // Underlying hash state from sodium
  int alive;  // Is the context still valid for updates/finalizes?
  int outlen; // Final size of the hash

} enacl_generichash_ctx;

static ErlNifResourceType *enacl_generic_hash_ctx_rtype;

static void enacl_generic_hash_ctx_dtor(ErlNifEnv *env,
                                        enacl_generichash_ctx *);

int enacl_init_generic_hash_ctx(ErlNifEnv *env) {
  enacl_generic_hash_ctx_rtype =
      enif_open_resource_type(env, NULL, "enacl_generichash_context",
                              (ErlNifResourceDtor *)enacl_generic_hash_ctx_dtor,
                              ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER, NULL);

  if (enacl_generic_hash_ctx_rtype == NULL)
    return 0;

  return 1;
}

static void enacl_generic_hash_ctx_dtor(ErlNifEnv *env,
                                        enacl_generichash_ctx *obj) {
  if (!obj->alive) {
    return;
  }

  if (obj->ctx)
    sodium_free(obj->ctx);

  if (obj->mtx != NULL)
    enif_mutex_destroy(obj->mtx);

  return;
}

/*
 * Generic hash
 */
ERL_NIF_TERM enacl_crypto_generichash_BYTES(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_generichash_BYTES);
}

ERL_NIF_TERM enacl_crypto_generichash_BYTES_MIN(ErlNifEnv *env, int argc,
                                                ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_generichash_BYTES_MIN);
}

ERL_NIF_TERM enacl_crypto_generichash_BYTES_MAX(ErlNifEnv *env, int argc,
                                                ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_generichash_BYTES_MAX);
}

ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES(ErlNifEnv *env, int argc,
                                               ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_generichash_KEYBYTES);
}

ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES_MIN(ErlNifEnv *env, int argc,
                                                   ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_generichash_KEYBYTES_MIN);
}

ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES_MAX(ErlNifEnv *env, int argc,
                                                   ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_generichash_KEYBYTES_MAX);
}

ERL_NIF_TERM enacl_crypto_generichash(ErlNifEnv *env, int argc,
                                      ERL_NIF_TERM const argv[]) {
  ErlNifBinary hash, message, key;
  unsigned hash_size;
  ERL_NIF_TERM ret;

  // Validate the arguments
  if (argc != 3)
    goto bad_arg;
  if (!enif_get_uint(env, argv[0], &hash_size))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[1], &message))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[2], &key))
    goto bad_arg;

  // Verify that hash size is
  // crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX
  if ((hash_size < crypto_generichash_BYTES_MIN) ||
      (hash_size > crypto_generichash_BYTES_MAX)) {
    goto bad_arg;
  }

  // validate key size
  unsigned char *k = key.data;
  if (0 == key.size) {
    k = NULL;
  } else if (key.size < crypto_generichash_KEYBYTES_MIN ||
             key.size > crypto_generichash_KEYBYTES_MAX) {
    goto bad_arg;
  }

  // allocate memory for hash
  if (!enif_alloc_binary(hash_size, &hash)) {
    goto err;
  }

  // calculate hash
  if (0 != crypto_generichash(hash.data, hash.size, message.data, message.size,
                              k, key.size)) {
    goto release;
  }

  ret = enif_make_binary(env, &hash);
  goto done;

bad_arg:
  return enif_make_badarg(env);
release:
  enif_release_binary(&hash);
err:
  ret = enacl_internal_error(env);
done:
  return ret;
}

ERL_NIF_TERM enacl_crypto_generichash_init(ErlNifEnv *env, int argc,
                                           ERL_NIF_TERM const argv[]) {
  ErlNifBinary key;
  unsigned hash_size;
  enacl_generichash_ctx *obj = NULL;
  ERL_NIF_TERM ret;

  // Validate the arguments
  if (argc != 2)
    goto bad_arg;
  if (!enif_get_uint(env, argv[0], &hash_size))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[1], &key))
    goto bad_arg;

  // Verify that hash size is valid
  if ((hash_size < crypto_generichash_BYTES_MIN) ||
      (hash_size > crypto_generichash_BYTES_MAX)) {
    goto bad_arg;
  }

  // validate key size
  unsigned char *k = key.data;
  if (0 == key.size) {
    k = NULL;
  } else if (key.size < crypto_generichash_KEYBYTES_MIN ||
             key.size > crypto_generichash_KEYBYTES_MAX) {
    goto bad_arg;
  }

  // Create the resource
  if ((obj = enif_alloc_resource(enacl_generic_hash_ctx_rtype,
                                 sizeof(enacl_generichash_ctx))) == NULL) {
    goto err;
  }

  // Allocate the state context via libsodium
  // Note that this ensures a 64byte alignment for the resource
  // And also protects the resource via guardpages
  obj->mtx = NULL;
  obj->ctx = NULL;
  obj->alive = 0;
  obj->outlen = 0;

  obj->ctx = (crypto_generichash_state *)sodium_malloc(
      crypto_generichash_statebytes());
  if (obj->ctx == NULL) {
    goto err;
  }
  obj->alive = 1;
  obj->outlen = hash_size;

  if ((obj->mtx = enif_mutex_create("enacl.generichash")) == NULL) {
    ret = enacl_error_tuple(env, "mutex_create");
    goto err;
  }

  // Call the library function
  if (0 != crypto_generichash_init(obj->ctx, k, key.size, obj->outlen)) {
    ret = enacl_error_tuple(env, "hash_init_error");
    goto err;
  }

  ret = enif_make_resource(env, obj);
  goto done;

bad_arg:
  return enif_make_badarg(env);
err:
  ret = enacl_internal_error(env);
  if (obj != NULL) {
    if (obj->alive) {
      sodium_free(obj->ctx);
      obj->alive = 0; // Maintain the invariant consistently
    }
  }
done:
  if (obj != NULL) {
    enif_release_resource(obj);
  }
  return ret;
}

ERL_NIF_TERM enacl_crypto_generichash_update(ErlNifEnv *env, int argc,
                                             ERL_NIF_TERM const argv[]) {
  ERL_NIF_TERM ret;
  ErlNifBinary data;
  unsigned int data_size;
  enacl_generichash_ctx *obj = NULL;

  // Validate the arguments
  if (argc != 2)
    goto bad_arg;
  if (!enif_get_resource(env, argv[0],
                         (ErlNifResourceType *)enacl_generic_hash_ctx_rtype,
                         (void **)&obj))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[1], &data))
    goto bad_arg;

  enif_mutex_lock(obj->mtx);
  if (!obj->alive) {
    goto err;
  }

  // Update hash state
  if (0 != crypto_generichash_update(obj->ctx, data.data, data.size)) {
    goto err;
  }

  ret = argv[0];
  goto done;

bad_arg:
  return enif_make_badarg(env);
err:
  ret = enacl_error_finalized(env);
done:
  enif_mutex_unlock(obj->mtx);
  return ret;
}

ERL_NIF_TERM enacl_crypto_generichash_final(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]) {
  ERL_NIF_TERM ret;
  ErlNifBinary hash;
  enacl_generichash_ctx *obj = NULL;

  if (argc != 1)
    goto bad_arg;
  if (!enif_get_resource(env, argv[0], enacl_generic_hash_ctx_rtype,
                         (void **)&obj))
    goto bad_arg;

  enif_mutex_lock(obj->mtx);
  if (!obj->alive) {
    ret = enacl_error_finalized(env);
    goto done;
  }

  if (!enif_alloc_binary(obj->outlen, &hash)) {
    goto err;
  }

  if (0 != crypto_generichash_final(obj->ctx, hash.data, hash.size)) {
    goto release;
  }

  // Finalize the object such that it cannot be reused by accident
  if (obj->ctx)
    sodium_free(obj->ctx);
  obj->alive = 0;

  ret = enif_make_binary(env, &hash);
  goto done;

bad_arg:
  return enif_make_badarg(env);
release:
  enif_release_binary(&hash);
err:
  ret = enacl_internal_error(env);
done:
  enif_mutex_unlock(obj->mtx);
  return ret;
}


================================================
FILE: c_src/generichash.h
================================================
#ifndef ENACL_GENERICHASH_H
#define ENACL_GENERICHASH_H

#include <erl_nif.h>

int enacl_init_generic_hash_ctx(ErlNifEnv *env);

ERL_NIF_TERM enacl_crypto_generichash_BYTES(ErlNifEnv *env, int argc,
                                            const ERL_NIF_TERM argv[]);
ERL_NIF_TERM enacl_crypto_generichash_BYTES_MIN(ErlNifEnv *env, int argc,
                                                const ERL_NIF_TERM argv[]);
ERL_NIF_TERM enacl_crypto_generichash_BYTES_MAX(ErlNifEnv *env, int argc,
                                                const ERL_NIF_TERM argv[]);

ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES(ErlNifEnv *env, int argc,
                                               const ERL_NIF_TERM argv[]);
ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES_MIN(ErlNifEnv *env, int argc,
                                                   const ERL_NIF_TERM argv[]);
ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES_MAX(ErlNifEnv *env, int argc,
                                                   const ERL_NIF_TERM argv[]);

ERL_NIF_TERM enacl_crypto_generichash(ErlNifEnv *env, int argc,
                                      const ERL_NIF_TERM argv[]);

ERL_NIF_TERM enacl_crypto_generichash_init(ErlNifEnv *env, int argc,
                                           const ERL_NIF_TERM argv[]);
ERL_NIF_TERM enacl_crypto_generichash_update(ErlNifEnv *env, int argc,
                                             const ERL_NIF_TERM argv[]);
ERL_NIF_TERM enacl_crypto_generichash_final(ErlNifEnv *env, int argc,
                                            const ERL_NIF_TERM argv[]);

#endif

================================================
FILE: c_src/hash.c
================================================
#include <sodium.h>

#include <erl_nif.h>

#include "enacl.h"
#include "hash.h"

ERL_NIF_TERM enacl_crypto_shorthash_BYTES(ErlNifEnv *env, int argc,
                                          ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_shorthash_BYTES);
}

ERL_NIF_TERM enacl_crypto_shorthash_KEYBYTES(ErlNifEnv *env, int argc,
                                             ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_shorthash_KEYBYTES);
}

ERL_NIF_TERM enacl_crypto_shorthash(ErlNifEnv *env, int argc,
                                    ERL_NIF_TERM const argv[]) {
  ErlNifBinary a, m, k;

  if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
      (!enif_inspect_binary(env, argv[1], &k))) {
    return enif_make_badarg(env);
  }

  if (k.size != crypto_shorthash_KEYBYTES) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(crypto_shorthash_BYTES, &a)) {
    return enacl_internal_error(env);
  }

  crypto_shorthash(a.data, m.data, m.size, k.data);

  return enif_make_binary(env, &a);
}

ERL_NIF_TERM enacl_crypto_hash(ErlNifEnv *env, int argc,
                               ERL_NIF_TERM const argv[]) {
  ErlNifBinary input;
  ErlNifBinary result;
  ERL_NIF_TERM ret;

  if ((argc != 1) || (!enif_inspect_iolist_as_binary(env, argv[0], &input)))
    goto bad_arg;

  if (!enif_alloc_binary(crypto_hash_BYTES, &result))
    goto err;

  crypto_hash(result.data, input.data, input.size);
  ret = enif_make_binary(env, &result);
  goto done;

bad_arg:
  return enif_make_badarg(env);
err:
  ret = enacl_internal_error(env);
done:
  return ret;
}


================================================
FILE: c_src/hash.h
================================================
#ifndef ENACL_HASH_H
#define ENACL_HASH_H

#include <erl_nif.h>

ERL_NIF_TERM enacl_crypto_shorthash_BYTES(ErlNifEnv *env, int argc,
                                          ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_shorthash_KEYBYTES(ErlNifEnv *env, int argc,
                                             ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_shorthash(ErlNifEnv *env, int argc,
                                    ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_hash(ErlNifEnv *env, int argc,
                               const ERL_NIF_TERM argv[]);
#endif

================================================
FILE: c_src/kdf.c
================================================
#include <sodium.h>

#include <erl_nif.h>

#include "enacl.h"
#include "kdf.h"


ERL_NIF_TERM enacl_crypto_kdf_KEYBYTES(ErlNifEnv *env, int argc,
                                       ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_kdf_KEYBYTES);
}

ERL_NIF_TERM enacl_crypto_kdf_CONTEXTBYTES(ErlNifEnv *env, int argc,
                                       ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_kdf_CONTEXTBYTES);
}


ERL_NIF_TERM enacl_crypto_kdf_derive_from_key(ErlNifEnv *env, int argc,
                                              ERL_NIF_TERM const argv[]) {
  ErlNifBinary m, c, r;
  uint64_t id;

  // Validate the arguments
  if ((argc != 3) ||
      (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
      (!enif_inspect_binary(env, argv[1], &c)) ||
      (!enif_get_uint64(env, argv[2], &id))) {
    return enif_make_badarg(env);
  }

  // Check Master Key length
  if (m.size != crypto_kdf_KEYBYTES) {
    return enif_make_badarg(env);
  }

  // Check Context Key length
  if (c.size != crypto_kdf_CONTEXTBYTES) {
    return enif_make_badarg(env);
  }

  // Allocate memory for return binary
  if (!enif_alloc_binary(crypto_kdf_KEYBYTES, &r)) {
    return enacl_internal_error(env);
  }

  if (crypto_kdf_derive_from_key(r.data, r.size,
                    id,
                    (const char *)c.data,
                    m.data) != 0) {
    /* out of memory */
    enif_release_binary(&r);
    return enacl_internal_error(env);
  }

  return enif_make_binary(env, &r);
}



================================================
FILE: c_src/kdf.h
================================================
#ifndef ENACL_KDF_H
#define ENACL_KDF_H

#include <erl_nif.h>

ERL_NIF_TERM enacl_crypto_kdf_KEYBYTES(ErlNifEnv *env, int argc,
                                       ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_kdf_CONTEXTBYTES(ErlNifEnv *env, int argc,
                                           ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_kdf_derive_from_key(ErlNifEnv *env, int argc,
                                              ERL_NIF_TERM const argv[]);

#endif


================================================
FILE: c_src/kx.c
================================================
#include <sodium.h>

#include <erl_nif.h>

#include "enacl.h"
#include "kx.h"

/* Key exchange */

ERL_NIF_TERM enacl_crypto_kx_SECRETKEYBYTES(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_kx_SECRETKEYBYTES);
}

ERL_NIF_TERM enacl_crypto_kx_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_kx_PUBLICKEYBYTES);
}

ERL_NIF_TERM enacl_crypto_kx_SESSIONKEYBYTES(ErlNifEnv *env, int argc,
                                             ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_kx_SESSIONKEYBYTES);
}

ERL_NIF_TERM enacl_crypto_kx_keypair(ErlNifEnv *env, int argc,
                                     ERL_NIF_TERM const argv[]) {
  ErlNifBinary pk, sk;

  if (argc != 0) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(crypto_kx_PUBLICKEYBYTES, &pk)) {
    return enacl_internal_error(env);
  }

  if (!enif_alloc_binary(crypto_kx_SECRETKEYBYTES, &sk)) {
    enif_release_binary(&pk);
    return enacl_internal_error(env);
  }

  crypto_kx_keypair(pk.data, sk.data);

  return enif_make_tuple2(env, enif_make_binary(env, &pk),
                          enif_make_binary(env, &sk));
}

ERL_NIF_TERM
enacl_crypto_kx_server_session_keys(ErlNifEnv *env, int argc,
                                    ERL_NIF_TERM const argv[]) {
  ERL_NIF_TERM ret;
  ErlNifBinary rx, tx, server_pk, server_sk, client_pk;

  if (argc != 3)
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[0], &server_pk))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[1], &server_sk))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[2], &client_pk))
    goto bad_arg;

  if (server_pk.size != crypto_kx_PUBLICKEYBYTES)
    goto bad_arg;
  if (server_sk.size != crypto_kx_SECRETKEYBYTES)
    goto bad_arg;
  if (client_pk.size != crypto_kx_PUBLICKEYBYTES)
    goto bad_arg;

  if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &rx)) {
    ret = enacl_internal_error(env);
    goto done;
  }

  if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &tx)) {
    ret = enacl_internal_error(env);
    goto release_rx;
  }

  if (0 != crypto_kx_server_session_keys(rx.data, tx.data, server_pk.data,
                                         server_sk.data, client_pk.data)) {
    // suspicious client public key
    ret = enacl_error_tuple(env, "invalid_client_public_key");
    goto release_tx_rx;
  }

  ret = enif_make_tuple2(env, enif_make_binary(env, &rx),
                         enif_make_binary(env, &tx));
  goto done;

bad_arg:
  return enif_make_badarg(env);
release_tx_rx:
  enif_release_binary(&tx);
release_rx:
  enif_release_binary(&rx);
done:
  return ret;
}

ERL_NIF_TERM
enacl_crypto_kx_client_session_keys(ErlNifEnv *env, int argc,
                                    ERL_NIF_TERM const argv[]) {
  ErlNifBinary rx, tx, client_pk, client_sk, server_pk;
  ERL_NIF_TERM ret;

  if (argc != 3)
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[0], &client_pk))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[1], &client_sk))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[2], &server_pk))
    goto bad_arg;

  if (client_pk.size != crypto_kx_PUBLICKEYBYTES)
    goto bad_arg;
  if (client_sk.size != crypto_kx_SECRETKEYBYTES)
    goto bad_arg;
  if (server_pk.size != crypto_kx_PUBLICKEYBYTES)
    goto bad_arg;

  if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &rx)) {
    ret = enacl_internal_error(env);
    goto done;
  }

  if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &tx)) {
    ret = enacl_internal_error(env);
    goto release_rx;
  }

  if (0 != crypto_kx_client_session_keys(rx.data, tx.data, client_pk.data,
                                         client_sk.data, server_pk.data)) {
    // suspicious server public key
    ret = enacl_error_tuple(env, "invalid_server_public_key");
    goto release_tx_rx;
  }

  ret = enif_make_tuple2(env, enif_make_binary(env, &rx),
                         enif_make_binary(env, &tx));
  goto done;
bad_arg:
  return enif_make_badarg(env);
release_tx_rx:
  enif_release_binary(&tx);
release_rx:
  enif_release_binary(&rx);
done:
  return ret;
}


================================================
FILE: c_src/kx.h
================================================
#ifndef ENACL_KX_H
#define ENACL_KX_H

#include <erl_nif.h>

ERL_NIF_TERM enacl_crypto_kx_SECRETKEYBYTES(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_kx_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_kx_SESSIONKEYBYTES(ErlNifEnv *env, int argc,
                                             ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_kx_keypair(ErlNifEnv *env, int argc,
                                     ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_kx_server_session_keys(ErlNifEnv *env, int argc,
                                                 ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_kx_client_session_keys(ErlNifEnv *env, int argc,
                                                 ERL_NIF_TERM const argv[]);

#endif


================================================
FILE: c_src/public.c
================================================
#include <sodium.h>

#include <erl_nif.h>

#include "enacl.h"
#include "public.h"

/* Public-key cryptography */
ERL_NIF_TERM enacl_crypto_box_NONCEBYTES(ErlNifEnv *env, int argc,
                                         ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_box_NONCEBYTES);
}

ERL_NIF_TERM enacl_crypto_box_ZEROBYTES(ErlNifEnv *env, int argc,
                                        ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_box_ZEROBYTES);
}

ERL_NIF_TERM enacl_crypto_box_BOXZEROBYTES(ErlNifEnv *env, int argc,
                                           ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_box_BOXZEROBYTES);
}

ERL_NIF_TERM enacl_crypto_box_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
                                             ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_box_PUBLICKEYBYTES);
}

ERL_NIF_TERM enacl_crypto_box_SECRETKEYBYTES(ErlNifEnv *env, int argc,
                                             ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_box_SECRETKEYBYTES);
}

ERL_NIF_TERM enacl_crypto_box_BEFORENMBYTES(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_box_BEFORENMBYTES);
}

ERL_NIF_TERM enacl_crypto_box_SEALBYTES(ErlNifEnv *env, int argc,
                                        ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_box_SEALBYTES);
}

ERL_NIF_TERM enacl_crypto_box_keypair(ErlNifEnv *env, int argc,
                                      ERL_NIF_TERM const argv[]) {
  ErlNifBinary pk, sk;

  if (argc != 0) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(crypto_box_PUBLICKEYBYTES, &pk)) {
    return enacl_error_tuple(env, "alloc_failed");
  }

  if (!enif_alloc_binary(crypto_box_SECRETKEYBYTES, &sk)) {
    enif_release_binary(&pk);
    return enacl_error_tuple(env, "alloc_failed");
  }

  crypto_box_keypair(pk.data, sk.data);

  return enif_make_tuple2(env, enif_make_binary(env, &pk),
                          enif_make_binary(env, &sk));
}

ERL_NIF_TERM enacl_crypto_box(ErlNifEnv *env, int argc,
                              ERL_NIF_TERM const argv[]) {
  ErlNifBinary padded_msg, nonce, pk, sk, result;
  ERL_NIF_TERM ret;

  if (argc != 4)
    goto bad_arg;
  if (!enif_inspect_iolist_as_binary(env, argv[0], &padded_msg))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[1], &nonce))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[2], &pk))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[3], &sk))
    goto bad_arg;

  if (nonce.size != crypto_box_NONCEBYTES)
    goto bad_arg;
  if (pk.size != crypto_box_PUBLICKEYBYTES)
    goto bad_arg;
  if (sk.size != crypto_box_SECRETKEYBYTES)
    goto bad_arg;
  if (padded_msg.size < crypto_box_ZEROBYTES)
    goto bad_arg;

  if (!enif_alloc_binary(padded_msg.size, &result)) {
    goto done;
  }

  if (0 != crypto_box(result.data, padded_msg.data, padded_msg.size, nonce.data,
                      pk.data, sk.data)) {
    goto release;
  }

  ret = enif_make_sub_binary(env, enif_make_binary(env, &result),
                             crypto_box_BOXZEROBYTES,
                             padded_msg.size - crypto_box_BOXZEROBYTES);

  goto done;

bad_arg:
  return enif_make_badarg(env);
release:
  enif_release_binary(&result);
err:
  ret = enacl_internal_error(env);
done:
  return ret;
}

ERL_NIF_TERM enacl_crypto_box_open(ErlNifEnv *env, int argc,
                                   ERL_NIF_TERM const argv[]) {
  ErlNifBinary padded_ciphertext, nonce, pk, sk, result;

  if ((argc != 4) ||
      (!enif_inspect_iolist_as_binary(env, argv[0], &padded_ciphertext)) ||
      (!enif_inspect_binary(env, argv[1], &nonce)) ||
      (!enif_inspect_binary(env, argv[2], &pk)) ||
      (!enif_inspect_binary(env, argv[3], &sk))) {
    return enif_make_badarg(env);
  }

  if ((nonce.size != crypto_box_NONCEBYTES) ||
      (pk.size != crypto_box_PUBLICKEYBYTES) ||
      (sk.size != crypto_box_SECRETKEYBYTES) ||
      (padded_ciphertext.size < crypto_box_BOXZEROBYTES)) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(padded_ciphertext.size, &result)) {
    return enacl_internal_error(env);
  }

  if (0 != crypto_box_open(result.data, padded_ciphertext.data,
                           padded_ciphertext.size, nonce.data, pk.data,
                           sk.data)) {
    enif_release_binary(&result);
    return enacl_error_tuple(env, "failed_verification");
  }

  ERL_NIF_TERM ret_ok = enif_make_atom(env, ATOM_OK);
  ERL_NIF_TERM ret_bin = enif_make_sub_binary(
      env, enif_make_binary(env, &result), crypto_box_ZEROBYTES,
      padded_ciphertext.size - crypto_box_ZEROBYTES);

  return enif_make_tuple2(env, ret_ok, ret_bin);
}

/* Precomputed crypto boxes */

ERL_NIF_TERM enacl_crypto_box_beforenm(ErlNifEnv *env, int argc,
                                       ERL_NIF_TERM const argv[]) {
  ErlNifBinary k, pk, sk;

  if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &pk)) ||
      (!enif_inspect_binary(env, argv[1], &sk)) ||
      (pk.size != crypto_box_PUBLICKEYBYTES) ||
      (sk.size != crypto_box_SECRETKEYBYTES)) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(crypto_box_BEFORENMBYTES, &k)) {
    goto err;
  }

  if (0 != crypto_box_beforenm(k.data, pk.data, sk.data)) {
    // error
    enif_release_binary(&k);
    goto err;
  }

  return enif_make_binary(env, &k);
err:
  return enacl_internal_error(env);
}

ERL_NIF_TERM enacl_crypto_box_afternm(ErlNifEnv *env, int argc,
                                      ERL_NIF_TERM const argv[]) {
  ErlNifBinary result, m, nonce, k;

  if ((argc != 3) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
      (!enif_inspect_binary(env, argv[1], &nonce)) ||
      (!enif_inspect_binary(env, argv[2], &k)) ||
      (m.size < crypto_box_ZEROBYTES) ||
      (nonce.size != crypto_box_NONCEBYTES) ||
      (k.size != crypto_box_BEFORENMBYTES)) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(m.size, &result)) {
    return enacl_internal_error(env);
  }

  crypto_box_afternm(result.data, m.data, m.size, nonce.data, k.data);

  return enif_make_sub_binary(env, enif_make_binary(env, &result),
                              crypto_box_BOXZEROBYTES,
                              m.size - crypto_box_BOXZEROBYTES);
}

ERL_NIF_TERM enacl_crypto_box_open_afternm(ErlNifEnv *env, int argc,
                                           ERL_NIF_TERM const argv[]) {
  ErlNifBinary result, m, nonce, k;

  if ((argc != 3) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
      (!enif_inspect_binary(env, argv[1], &nonce)) ||
      (!enif_inspect_binary(env, argv[2], &k)) ||
      (m.size < crypto_box_BOXZEROBYTES) ||
      (nonce.size != crypto_box_NONCEBYTES) ||
      (k.size != crypto_box_BEFORENMBYTES)) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(m.size, &result)) {
    return enacl_internal_error(env);
  }

  if (0 != crypto_box_open_afternm(result.data, m.data, m.size, nonce.data,
                                   k.data)) {
    enif_release_binary(&result);
    return enacl_error_tuple(env, "failed_verification");
  }

  ERL_NIF_TERM ret_ok = enif_make_atom(env, ATOM_OK);
  ERL_NIF_TERM ret_bin =
      enif_make_sub_binary(env, enif_make_binary(env, &result),
                           crypto_box_ZEROBYTES, m.size - crypto_box_ZEROBYTES);
  return enif_make_tuple2(env, ret_ok, ret_bin);
}

/* Sealed box functions */

ERL_NIF_TERM enacl_crypto_box_seal(ErlNifEnv *env, int argc,
                                   ERL_NIF_TERM const argv[]) {
  ErlNifBinary key, msg, ciphertext;

  if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &msg)) ||
      (!enif_inspect_binary(env, argv[1], &key))) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(msg.size + crypto_box_SEALBYTES, &ciphertext)) {
    return enacl_internal_error(env);
  }

  crypto_box_seal(ciphertext.data, msg.data, msg.size, key.data);

  return enif_make_binary(env, &ciphertext);
}

ERL_NIF_TERM enacl_crypto_box_seal_open(ErlNifEnv *env, int argc,
                                        ERL_NIF_TERM const argv[]) {
  ErlNifBinary pk, sk, ciphertext, msg;

  if ((argc != 3) ||
      (!enif_inspect_iolist_as_binary(env, argv[0], &ciphertext)) ||
      (!enif_inspect_binary(env, argv[1], &pk)) ||
      (!enif_inspect_binary(env, argv[2], &sk))) {
    return enif_make_badarg(env);
  }

  if (ciphertext.size < crypto_box_SEALBYTES) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(ciphertext.size - crypto_box_SEALBYTES, &msg)) {
    return enacl_internal_error(env);
  }

  if (crypto_box_seal_open(msg.data, ciphertext.data, ciphertext.size, pk.data,
                           sk.data) != 0) {
    enif_release_binary(&msg);
    return enacl_error_tuple(env, "failed_verification");
  }

  ERL_NIF_TERM ret_ok = enif_make_atom(env, ATOM_OK);
  ERL_NIF_TERM ret_bin = enif_make_binary(env, &msg);

  return enif_make_tuple2(env, ret_ok, ret_bin);
}


================================================
FILE: c_src/public.h
================================================
#ifndef ENACL_PUBLIC_H
#define ENACL_PUBLIC_H

ERL_NIF_TERM enacl_crypto_box_NONCEBYTES(ErlNifEnv *env, int argc,
                                         ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_box_ZEROBYTES(ErlNifEnv *env, int argc,
                                        ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_box_BOXZEROBYTES(ErlNifEnv *env, int argc,
                                           ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_box_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
                                             ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_box_SECRETKEYBYTES(ErlNifEnv *env, int argc,
                                             ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_box_BEFORENMBYTES(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_box_keypair(ErlNifEnv *env, int argc,
                                      ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_box(ErlNifEnv *env, int argc,
                              ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_box_open(ErlNifEnv *env, int argc,
                                   ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_box_beforenm(ErlNifEnv *env, int argc,
                                       ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_box_afternm(ErlNifEnv *env, int argc,
                                      ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_box_open_afternm(ErlNifEnv *env, int argc,
                                           ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_box_SEALBYTES(ErlNifEnv *env, int argc,
                                        ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_box_seal(ErlNifEnv *env, int argc,
                                   ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_box_seal_open(ErlNifEnv *env, int argc,
                                        ERL_NIF_TERM const argv[]);

#endif

================================================
FILE: c_src/pwhash.c
================================================
#include <sodium.h>

#include <erl_nif.h>

#include "enacl.h"
#include "pwhash.h"

static size_t enacl_pwhash_opslimit(ErlNifEnv *env, ERL_NIF_TERM arg) {
  ERL_NIF_TERM a;
  size_t r;

  if (enif_is_atom(env, arg)) {
    a = enif_make_atom(env, "interactive");
    if (enif_is_identical(a, arg)) {
      return crypto_pwhash_OPSLIMIT_INTERACTIVE;
    }

    a = enif_make_atom(env, "moderate");
    if (enif_is_identical(a, arg)) {
      return crypto_pwhash_OPSLIMIT_MODERATE;
    }

    a = enif_make_atom(env, "sensitive");
    if (enif_is_identical(a, arg)) {
      return crypto_pwhash_OPSLIMIT_SENSITIVE;
    }
  } else if (enif_get_ulong(env, arg, &r)) {
    return r;
  }

  return 0;
}

static size_t enacl_pwhash_memlimit(ErlNifEnv *env, ERL_NIF_TERM arg) {
  ERL_NIF_TERM a;
  size_t r;

  if (enif_is_atom(env, arg)) {
    a = enif_make_atom(env, "interactive");
    if (enif_is_identical(a, arg)) {
      return crypto_pwhash_MEMLIMIT_INTERACTIVE;
    }

    a = enif_make_atom(env, "moderate");
    if (enif_is_identical(a, arg)) {
      return crypto_pwhash_MEMLIMIT_MODERATE;
    }

    a = enif_make_atom(env, "sensitive");
    if (enif_is_identical(a, arg)) {
      return crypto_pwhash_MEMLIMIT_SENSITIVE;
    }
  } else if (enif_get_ulong(env, arg, &r)) {
    return r;
  }

  return 0;
}

ERL_NIF_TERM enacl_crypto_pwhash_SALTBYTES(ErlNifEnv *env, int argc,
                                           ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_pwhash_SALTBYTES);
}

static int enacl_pwhash_alg(ErlNifEnv *env, ERL_NIF_TERM arg) {
  ERL_NIF_TERM a;
  int r;

  if (enif_is_atom(env, arg)) {
    a = enif_make_atom(env, "default");
    if (enif_is_identical(a, arg)) {
      return crypto_pwhash_ALG_DEFAULT;
    }

    a = enif_make_atom(env, "argon2i13");
    if (enif_is_identical(a, arg)) {
      return crypto_pwhash_ALG_ARGON2I13;
    }

    a = enif_make_atom(env, "argon2id13");
    if (enif_is_identical(a, arg)) {
      return crypto_pwhash_ALG_ARGON2ID13;
    }
  } else if (enif_get_int(env, arg, &r)) {
    return r;
  }

  return 0;
}

ERL_NIF_TERM enacl_crypto_pwhash(ErlNifEnv *env, int argc,
                                 ERL_NIF_TERM const argv[]) {
  ErlNifBinary h, p, s;
  size_t o, m;
  int alg;

  // Validate the arguments
  if ((argc != 5) || (!enif_inspect_iolist_as_binary(env, argv[0], &p)) ||
      (!enif_inspect_binary(env, argv[1], &s)) ||
      !(o = enacl_pwhash_opslimit(env, argv[2])) ||
      !(m = enacl_pwhash_memlimit(env, argv[3])) ||
      !(alg = enacl_pwhash_alg(env, argv[4]))) {
    return enif_make_badarg(env);
  }

  // Check limits
  if ((o < crypto_pwhash_OPSLIMIT_MIN) || (o > crypto_pwhash_OPSLIMIT_MAX) ||
      (m < crypto_pwhash_MEMLIMIT_MIN) || (m > crypto_pwhash_MEMLIMIT_MAX)) {
    return enif_make_badarg(env);
  }

  // Check Salt size
  if (s.size != crypto_pwhash_SALTBYTES) {
    return enif_make_badarg(env);
  }

  // Allocate memory for return binary
  if (!enif_alloc_binary(crypto_box_SEEDBYTES, &h)) {
    return enacl_internal_error(env);
  }

  if (crypto_pwhash(h.data, h.size, (char *)p.data, p.size, s.data, o, m,
                    alg) != 0) {
    /* out of memory */
    enif_release_binary(&h);
    return enacl_internal_error(env);
  }

  return enif_make_binary(env, &h);
}

ERL_NIF_TERM enacl_crypto_pwhash_str(ErlNifEnv *env, int argc,
                                     ERL_NIF_TERM const argv[]) {
  ErlNifBinary h, p;
  size_t o, m;

  // Validate the arguments
  if ((argc != 3) || (!enif_inspect_iolist_as_binary(env, argv[0], &p)) ||
      !(o = enacl_pwhash_opslimit(env, argv[1])) ||
      !(m = enacl_pwhash_memlimit(env, argv[2]))) {
    return enif_make_badarg(env);
  }

  // Check limits
  if ((o < crypto_pwhash_OPSLIMIT_MIN) || (o > crypto_pwhash_OPSLIMIT_MAX) ||
      (m < crypto_pwhash_MEMLIMIT_MIN) || (m > crypto_pwhash_MEMLIMIT_MAX)) {
    return enif_make_badarg(env);
  }

  // Allocate memory for return binary
  if (!enif_alloc_binary(crypto_pwhash_STRBYTES, &h)) {
    return enacl_internal_error(env);
  }

  if (crypto_pwhash_str((char *)h.data, (char *)p.data, p.size, o, m) != 0) {
    /* out of memory */
    enif_release_binary(&h);
    return enacl_internal_error(env);
  }

  return enif_make_binary(env, &h);
}

ERL_NIF_TERM enacl_crypto_pwhash_str_verify(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]) {
  ErlNifBinary h, p;
  // Validate the arguments
  if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &h)) ||
      (!enif_inspect_iolist_as_binary(env, argv[1], &p))) {
    return enif_make_badarg(env);
  }

  ERL_NIF_TERM ret = enif_make_atom(env, ATOM_TRUE);
  if (crypto_pwhash_str_verify((char *)h.data, (char *)p.data, p.size) != 0) {
    /* wrong password */
    ret = enif_make_atom(env, ATOM_FALSE);
  }

  return ret;
}


================================================
FILE: c_src/pwhash.h
================================================
#ifndef ENACL_PWHASH_H
#define ENACL_PWHASH_H

#include <erl_nif.h>

ERL_NIF_TERM enacl_crypto_pwhash_SALTBYTES(ErlNifEnv *env, int argc,
                                           ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_pwhash(ErlNifEnv *env, int argc,
                                 ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_pwhash_str(ErlNifEnv *env, int argc,
                                     ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_pwhash_str_verify(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]);

#endif


================================================
FILE: c_src/randombytes.c
================================================
#include <sodium.h>

#include <erl_nif.h>

#include "enacl.h"
#include "randombytes.h"

ERL_NIF_TERM enif_randombytes(ErlNifEnv *env, int argc,
                              ERL_NIF_TERM const argv[]) {
  unsigned req_size;
  ErlNifBinary result;

  if ((argc != 1) || (!enif_get_uint(env, argv[0], &req_size))) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(req_size, &result)) {
    return enacl_internal_error(env);
  }

  randombytes(result.data, result.size);

  return enif_make_binary(env, &result);
}

ERL_NIF_TERM enif_randombytes_uint32(ErlNifEnv *env, int argc,
                                     ERL_NIF_TERM const argv[]) {
  ErlNifUInt64 result;

  if (argc != 0) {
    return enif_make_badarg(env);
  }

  result = randombytes_random();
  return enif_make_uint64(env, result);
}

ERL_NIF_TERM enif_randombytes_uniform(ErlNifEnv *env, int argc,
                                      ERL_NIF_TERM const argv[]) {
  unsigned upper_bound;
  ErlNifUInt64 result;

  if ((argc != 1) || (!enif_get_uint(env, argv[0], &upper_bound))) {
    return enif_make_badarg(env);
  }

  result = randombytes_uniform(upper_bound);
  return enif_make_uint64(env, result);
}


================================================
FILE: c_src/randombytes.h
================================================
#ifndef ENACL_RANDOMBYTES_H
#define ENACL_RANDOMBYTES_H

#include <erl_nif.h>

ERL_NIF_TERM enif_randombytes(ErlNifEnv *env, int argc,
                              ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enif_randombytes_uint32(ErlNifEnv *env, int argc,
                                     ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enif_randombytes_uniform(ErlNifEnv *env, int argc,
                                      ERL_NIF_TERM const argv[]);

#endif

================================================
FILE: c_src/secret.c
================================================
#include <sodium.h>

#include <erl_nif.h>

#include "enacl.h"
#include "secret.h"

/* Secret key cryptography */

ERL_NIF_TERM
enacl_crypto_secretbox_NONCEBYTES(ErlNifEnv *env, int argc,
                                  ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_secretbox_NONCEBYTES);
}

ERL_NIF_TERM enacl_crypto_secretbox_KEYBYTES(ErlNifEnv *env, int argc,
                                             ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_secretbox_KEYBYTES);
}

ERL_NIF_TERM
enacl_crypto_secretbox_ZEROBYTES(ErlNifEnv *env, int argc,
                                 ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_secretbox_ZEROBYTES);
}

ERL_NIF_TERM
enacl_crypto_secretbox_BOXZEROBYTES(ErlNifEnv *env, int argc,
                                    ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_secretbox_BOXZEROBYTES);
}

ERL_NIF_TERM
enacl_crypto_stream_chacha20_KEYBYTES(ErlNifEnv *env, int argc,
                                      ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_stream_chacha20_KEYBYTES);
}

ERL_NIF_TERM
enacl_crypto_stream_chacha20_NONCEBYTES(ErlNifEnv *env, int argc,
                                        ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_stream_chacha20_NONCEBYTES);
}

ERL_NIF_TERM enacl_crypto_stream_KEYBYTES(ErlNifEnv *env, int argc,
                                          ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_stream_KEYBYTES);
}

ERL_NIF_TERM enacl_crypto_stream_NONCEBYTES(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_stream_NONCEBYTES);
}

ERL_NIF_TERM enacl_crypto_auth_BYTES(ErlNifEnv *env, int argc,
                                     ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_auth_BYTES);
}

ERL_NIF_TERM enacl_crypto_auth_KEYBYTES(ErlNifEnv *env, int argc,
                                        ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_auth_KEYBYTES);
}

ERL_NIF_TERM enacl_crypto_onetimeauth_BYTES(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_onetimeauth_BYTES);
}

ERL_NIF_TERM
enacl_crypto_onetimeauth_KEYBYTES(ErlNifEnv *env, int argc,
                                  ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_onetimeauth_KEYBYTES);
}

ERL_NIF_TERM enacl_crypto_secretbox(ErlNifEnv *env, int argc,
                                    ERL_NIF_TERM const argv[]) {
  ErlNifBinary key, nonce, padded_msg, padded_ciphertext;

  if ((argc != 3) ||
      (!enif_inspect_iolist_as_binary(env, argv[0], &padded_msg)) ||
      (!enif_inspect_binary(env, argv[1], &nonce)) ||
      (!enif_inspect_binary(env, argv[2], &key))) {
    return enif_make_badarg(env);
  }

  if ((key.size != crypto_secretbox_KEYBYTES) ||
      (nonce.size != crypto_secretbox_NONCEBYTES) ||
      (padded_msg.size < crypto_secretbox_ZEROBYTES)) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(padded_msg.size, &padded_ciphertext)) {
    return enacl_internal_error(env);
  }

  crypto_secretbox(padded_ciphertext.data, padded_msg.data, padded_msg.size,
                   nonce.data, key.data);

  return enif_make_sub_binary(env, enif_make_binary(env, &padded_ciphertext),
                              crypto_secretbox_BOXZEROBYTES,
                              padded_msg.size - crypto_secretbox_BOXZEROBYTES);
}

ERL_NIF_TERM enacl_crypto_secretbox_open(ErlNifEnv *env, int argc,
                                         ERL_NIF_TERM const argv[]) {
  ErlNifBinary key, nonce, padded_ciphertext, padded_msg;

  if ((argc != 3) ||
      (!enif_inspect_iolist_as_binary(env, argv[0], &padded_ciphertext)) ||
      (!enif_inspect_binary(env, argv[1], &nonce)) ||
      (!enif_inspect_binary(env, argv[2], &key))) {
    return enif_make_badarg(env);
  }

  if ((key.size != crypto_secretbox_KEYBYTES) ||
      (nonce.size != crypto_secretbox_NONCEBYTES) ||
      (padded_ciphertext.size < crypto_secretbox_BOXZEROBYTES)) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(padded_ciphertext.size, &padded_msg)) {
    return enacl_internal_error(env);
  }

  if (crypto_secretbox_open(padded_msg.data, padded_ciphertext.data,
                            padded_ciphertext.size, nonce.data,
                            key.data) != 0) {
    enif_release_binary(&padded_msg);
    return enacl_error_tuple(env, "failed_verification");
  }

  ERL_NIF_TERM ret_ok = enif_make_atom(env, ATOM_OK);
  ERL_NIF_TERM ret_bin = enif_make_sub_binary(
      env, enif_make_binary(env, &padded_msg), crypto_secretbox_ZEROBYTES,
      padded_ciphertext.size - crypto_secretbox_ZEROBYTES);
  return enif_make_tuple2(env, ret_ok, ret_bin);
}

ERL_NIF_TERM enacl_crypto_stream_chacha20(ErlNifEnv *env, int argc,
                                          ERL_NIF_TERM const argv[]) {
  ErlNifBinary c, n, k;
  ErlNifUInt64 clen;

  if ((argc != 3) || (!enif_get_uint64(env, argv[0], &clen)) ||
      (!enif_inspect_binary(env, argv[1], &n)) ||
      (!enif_inspect_binary(env, argv[2], &k))) {
    return enif_make_badarg(env);
  }

  if ((k.size != crypto_stream_chacha20_KEYBYTES) ||
      (n.size != crypto_stream_chacha20_NONCEBYTES)) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(clen, &c)) {
    return enacl_internal_error(env);
  }

  crypto_stream_chacha20(c.data, c.size, n.data, k.data);

  return enif_make_binary(env, &c);
}

ERL_NIF_TERM
enacl_crypto_stream_chacha20_xor(ErlNifEnv *env, int argc,
                                 ERL_NIF_TERM const argv[]) {
  ErlNifBinary c, m, n, k;

  if ((argc != 3) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
      (!enif_inspect_binary(env, argv[1], &n)) ||
      (!enif_inspect_binary(env, argv[2], &k))) {
    return enif_make_badarg(env);
  }

  if ((k.size != crypto_stream_chacha20_KEYBYTES) ||
      (n.size != crypto_stream_chacha20_NONCEBYTES)) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(m.size, &c)) {
    return enacl_internal_error(env);
  }

  crypto_stream_chacha20_xor(c.data, m.data, m.size, n.data, k.data);

  return enif_make_binary(env, &c);
}

ERL_NIF_TERM enacl_crypto_stream(ErlNifEnv *env, int argc,
                                 ERL_NIF_TERM const argv[]) {
  ErlNifBinary c, n, k;
  ErlNifUInt64 clen;

  if ((argc != 3) || (!enif_get_uint64(env, argv[0], &clen)) ||
      (!enif_inspect_binary(env, argv[1], &n)) ||
      (!enif_inspect_binary(env, argv[2], &k))) {
    return enif_make_badarg(env);
  }

  if ((k.size != crypto_stream_KEYBYTES) ||
      (n.size != crypto_stream_NONCEBYTES)) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(clen, &c)) {
    return enacl_internal_error(env);
  }

  crypto_stream(c.data, c.size, n.data, k.data);

  return enif_make_binary(env, &c);
}

ERL_NIF_TERM enacl_crypto_stream_xor(ErlNifEnv *env, int argc,
                                     ERL_NIF_TERM const argv[]) {
  ErlNifBinary c, m, n, k;

  if ((argc != 3) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
      (!enif_inspect_binary(env, argv[1], &n)) ||
      (!enif_inspect_binary(env, argv[2], &k))) {
    return enif_make_badarg(env);
  }

  if ((k.size != crypto_stream_KEYBYTES) ||
      (n.size != crypto_stream_NONCEBYTES)) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(m.size, &c)) {
    return enacl_internal_error(env);
  }

  crypto_stream_xor(c.data, m.data, m.size, n.data, k.data);

  return enif_make_binary(env, &c);
}

ERL_NIF_TERM enacl_crypto_auth(ErlNifEnv *env, int argc,
                               ERL_NIF_TERM const argv[]) {
  ErlNifBinary a, m, k;

  if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
      (!enif_inspect_binary(env, argv[1], &k))) {
    return enif_make_badarg(env);
  }

  if (k.size != crypto_auth_KEYBYTES) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(crypto_auth_BYTES, &a)) {
    return enacl_internal_error(env);
  }

  crypto_auth(a.data, m.data, m.size, k.data);

  return enif_make_binary(env, &a);
}

ERL_NIF_TERM enacl_crypto_auth_verify(ErlNifEnv *env, int argc,
                                      ERL_NIF_TERM const argv[]) {
  ErlNifBinary a, m, k;

  if ((argc != 3) || (!enif_inspect_binary(env, argv[0], &a)) ||
      (!enif_inspect_iolist_as_binary(env, argv[1], &m)) ||
      (!enif_inspect_binary(env, argv[2], &k))) {
    return enif_make_badarg(env);
  }

  if ((k.size != crypto_auth_KEYBYTES) || (a.size != crypto_auth_BYTES)) {
    return enif_make_badarg(env);
  }

  if (0 == crypto_auth_verify(a.data, m.data, m.size, k.data)) {
    return enif_make_atom(env, "true");
  } else {
    return enif_make_atom(env, "false");
  }
}

ERL_NIF_TERM enacl_crypto_onetimeauth(ErlNifEnv *env, int argc,
                                      ERL_NIF_TERM const argv[]) {
  ErlNifBinary a, m, k;

  if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
      (!enif_inspect_binary(env, argv[1], &k))) {
    return enif_make_badarg(env);
  }

  if (k.size != crypto_onetimeauth_KEYBYTES) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(crypto_onetimeauth_BYTES, &a)) {
    return enacl_internal_error(env);
  }

  crypto_onetimeauth(a.data, m.data, m.size, k.data);

  return enif_make_binary(env, &a);
}

ERL_NIF_TERM enacl_crypto_onetimeauth_verify(ErlNifEnv *env, int argc,
                                             ERL_NIF_TERM const argv[]) {
  ErlNifBinary a, m, k;

  if ((argc != 3) || (!enif_inspect_binary(env, argv[0], &a)) ||
      (!enif_inspect_iolist_as_binary(env, argv[1], &m)) ||
      (!enif_inspect_binary(env, argv[2], &k))) {
    return enif_make_badarg(env);
  }

  if ((k.size != crypto_onetimeauth_KEYBYTES) ||
      (a.size != crypto_onetimeauth_BYTES)) {
    return enif_make_badarg(env);
  }

  if (0 == crypto_onetimeauth_verify(a.data, m.data, m.size, k.data)) {
    return enif_make_atom(env, "true");
  } else {
    return enif_make_atom(env, "false");
  }
}


================================================
FILE: c_src/secret.h
================================================
#ifndef ENACL_SECRET_H
#define ENACL_SECRET_H

ERL_NIF_TERM enacl_crypto_secretbox_NONCEBYTES(ErlNifEnv *env, int argc,
                                               ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_secretbox_KEYBYTES(ErlNifEnv *env, int argc,
                                             ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_secretbox_ZEROBYTES(ErlNifEnv *env, int argc,
                                              ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_secretbox_BOXZEROBYTES(ErlNifEnv *env, int argc,
                                                 ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_stream_chacha20_KEYBYTES(ErlNifEnv *env, int argc,
                                                   ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_stream_chacha20_NONCEBYTES(ErlNifEnv *env, int argc,
                                                     ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_stream_KEYBYTES(ErlNifEnv *env, int argc,
                                          ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_stream_NONCEBYTES(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_auth_BYTES(ErlNifEnv *env, int argc,
                                     ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_auth_KEYBYTES(ErlNifEnv *env, int argc,
                                        ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_onetimeauth_BYTES(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_onetimeauth_KEYBYTES(ErlNifEnv *env, int argc,
                                               ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_secretbox(ErlNifEnv *env, int argc,
                                    ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_secretbox_open(ErlNifEnv *env, int argc,
                                         ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_stream_chacha20(ErlNifEnv *env, int argc,
                                          ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_stream_chacha20_xor(ErlNifEnv *env, int argc,
                                              ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_stream(ErlNifEnv *env, int argc,
                                 ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_stream_xor(ErlNifEnv *env, int argc,
                                     ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_auth(ErlNifEnv *env, int argc,
                               ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_auth_verify(ErlNifEnv *env, int argc,
                                      ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_onetimeauth(ErlNifEnv *env, int argc,
                                      ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_onetimeauth_verify(ErlNifEnv *env, int argc,
                                             ERL_NIF_TERM const argv[]);

#endif


================================================
FILE: c_src/secretstream.c
================================================
#include <erl_nif.h>
#include <sodium.h>

#include "enacl.h"
#include "secretstream.h"

typedef struct enacl_secretstream_ctx {
  ErlNifMutex *mtx;
  crypto_secretstream_xchacha20poly1305_state
      *state; // The underlying secretstream state
  int alive;  // Is the context still valid for updates/finalization
} enacl_secretstream_ctx;

ErlNifResourceType *enacl_secretstream_ctx_rtype = NULL;

static void enacl_secretstream_ctx_dtor(ErlNifEnv *env,
                                        enacl_secretstream_ctx *);

int enacl_init_secretstream_ctx(ErlNifEnv *env) {
  enacl_secretstream_ctx_rtype =
      enif_open_resource_type(env, NULL, "enacl_secretstream_context",
                              (ErlNifResourceDtor *)enacl_secretstream_ctx_dtor,
                              ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER, NULL);

  if (enacl_secretstream_ctx_rtype == NULL)
    return 0;

  return 1;
}

static void enacl_secretstream_ctx_dtor(ErlNifEnv *env,
                                        enacl_secretstream_ctx *obj) {
  if (!obj->alive) {
    return;
  }

  if (obj->state)
    sodium_memzero(obj->state,
                   crypto_secretstream_xchacha20poly1305_statebytes());
  enif_free(obj->state);

  if (obj->mtx != NULL)
    enif_mutex_destroy(obj->mtx);

  return;
}

/*
 * Secretstream
 */

ERL_NIF_TERM
enacl_crypto_secretstream_xchacha20poly1305_ABYTES(ErlNifEnv *env, int argc,
                                                   const ERL_NIF_TERM argv[]) {
  return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_ABYTES);
}

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_HEADERBYTES(
    ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  return enif_make_int64(env,
                         crypto_secretstream_xchacha20poly1305_HEADERBYTES);
}

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_KEYBYTES(
    ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_KEYBYTES);
}

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX(
    ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  return enif_make_int64(
      env, crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX);
}

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_MESSAGE(
    ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {

  return enif_make_int64(env,
                         crypto_secretstream_xchacha20poly1305_TAG_MESSAGE);
}

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_PUSH(
    ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {

  return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_TAG_PUSH);
}

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_REKEY(
    ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {

  return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_TAG_REKEY);
}

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_FINAL(
    ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {

  return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_TAG_FINAL);
}

ERL_NIF_TERM
enacl_crypto_secretstream_xchacha20poly1305_keygen(ErlNifEnv *env, int argc,
                                                   const ERL_NIF_TERM argv[]) {

  ErlNifBinary key;

  if (argc != 0) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(crypto_secretstream_xchacha20poly1305_KEYBYTES,
                         &key)) {
    return enacl_internal_error(env);
  }

  crypto_secretstream_xchacha20poly1305_keygen(key.data);

  return enif_make_binary(env, &key);
}

/*
   int crypto_secretstream_xchacha20poly1305_init_push
      (crypto_secretstream_xchacha20poly1305_state *state,
      unsigned char out[crypto_secretstream_xchacha20poly1305_HEADERBYTES],
      const unsigned char k[crypto_secretstream_xchacha20poly1305_KEYBYTES])
*/
ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_init_push(
    ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  ERL_NIF_TERM ret;
  ErlNifBinary key, header;
  enacl_secretstream_ctx *obj = NULL;

  if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &key))) {
    goto bad_arg;
  }

  if (key.size != crypto_secretstream_xchacha20poly1305_KEYBYTES) {
    goto bad_arg;
  }

  if (!enif_alloc_binary(crypto_secretstream_xchacha20poly1305_HEADERBYTES,
                         &header)) {
    ret = enacl_internal_error(env);
    goto done;
  }

  if ((obj = enif_alloc_resource(enacl_secretstream_ctx_rtype,
                                 sizeof(enacl_secretstream_ctx))) == NULL) {
    ret = enacl_internal_error(env);
    goto release_header;
  }
  obj->alive = 0;
  obj->state = enif_alloc(crypto_secretstream_xchacha20poly1305_statebytes());

  if (obj->state == NULL) {
    ret = enacl_internal_error(env);
    goto release;
  }
  obj->alive = 1;

  if ((obj->mtx = enif_mutex_create("enacl.secretstream")) == NULL) {
    ret = enacl_internal_error(env);
    goto free;
  }

  crypto_secretstream_xchacha20poly1305_init_push(obj->state, header.data,
                                                  key.data);

  ret = enif_make_tuple2(env, enif_make_binary(env, &header),
                         enif_make_resource(env, obj));

  goto release;
bad_arg:
  return enif_make_badarg(env);
free:
  if (obj->alive)
    if (obj->state != NULL) {
      sodium_memzero(obj->state,
                     crypto_secretstream_xchacha20poly1305_statebytes());
      enif_free(obj->state);
      obj->state = NULL;
    }
release_header:
  enif_release_binary(&header);
release:
  // This also frees the mutex via the destructor
  enif_release_resource(obj);
done:
  return ret;
}

/*
crypto_secretstream_xchacha20poly1305_init_pull
   (crypto_secretstream_xchacha20poly1305_state *state,
    const unsigned char in[crypto_secretstream_xchacha20poly1305_HEADERBYTES],
    const unsigned char k[crypto_secretstream_xchacha20poly1305_KEYBYTES])
*/
ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_init_pull(
    ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  ERL_NIF_TERM ret;
  ErlNifBinary header, key;
  enacl_secretstream_ctx *obj = NULL;

  if (argc != 2) {
    goto bad_arg;
  }

  if (!enif_inspect_binary(env, argv[0], &header)) {
    goto bad_arg;
  }

  if (!enif_inspect_binary(env, argv[1], &key)) {
    goto bad_arg;
  }

  if ((key.size != crypto_secretstream_xchacha20poly1305_KEYBYTES) ||
      (header.size != crypto_secretstream_xchacha20poly1305_HEADERBYTES)) {
    goto bad_arg;
  }

  if ((obj = enif_alloc_resource(enacl_secretstream_ctx_rtype,
                                 sizeof(enacl_secretstream_ctx))) == NULL) {
    ret = enacl_internal_error(env);
    goto done;
  }

  obj->alive = 0;
  obj->state = enif_alloc(crypto_secretstream_xchacha20poly1305_statebytes());

  if (obj->state == NULL) {
    goto release;
  }
  obj->alive = 1;

  if ((obj->mtx = enif_mutex_create("enacl.secretstream")) == NULL) {
    goto free;
  }

  crypto_secretstream_xchacha20poly1305_init_pull(obj->state, header.data,
                                                  key.data);

  ret = enif_make_resource(env, obj);

  goto release;

bad_arg:
  return enif_make_badarg(env);
free:
  if (obj->alive)
    if (obj->state != NULL) {
      sodium_memzero(obj->state,
                     crypto_secretstream_xchacha20poly1305_statebytes());
      enif_free(obj->state);
      obj->state = NULL;
    }
release:
  // This also frees the mutex via the destructor
  enif_release_resource(obj);
done:
  return ret;
}

/*
void
crypto_secretstream_xchacha20poly1305_rekey
    (crypto_secretstream_xchacha20poly1305_state *state)
*/
ERL_NIF_TERM
enacl_crypto_secretstream_xchacha20poly1305_rekey(ErlNifEnv *env, int argc,
                                                  const ERL_NIF_TERM argv[]) {
  ERL_NIF_TERM ret;
  enacl_secretstream_ctx *obj = NULL;

  if (argc != 1) {
    goto bad_arg;
  }

  if (!enif_get_resource(env, argv[0],
                         (ErlNifResourceType *)enacl_secretstream_ctx_rtype,
                         (void **)&obj)) {
    goto bad_arg;
  }

  enif_mutex_lock(obj->mtx);
  if (!obj->alive) {
    goto err;
  }

  crypto_secretstream_xchacha20poly1305_rekey(obj->state);

  ret = enif_make_atom(env, ATOM_OK);

  goto done;

bad_arg:
  return enif_make_badarg(env);
err:
  ret = enacl_error_finalized(env);
done:
  enif_mutex_unlock(obj->mtx);
  return ret;
}

/*
int
crypto_secretstream_xchacha20poly1305_push
   (crypto_secretstream_xchacha20poly1305_state *state,
    unsigned char *out, unsigned long long *outlen_p,
    const unsigned char *m, unsigned long long mlen,
    const unsigned char *ad, unsigned long long adlen, unsigned char tag)
*/
ERL_NIF_TERM
enacl_crypto_secretstream_xchacha20poly1305_push(ErlNifEnv *env, int argc,
                                                 const ERL_NIF_TERM argv[]) {
  ERL_NIF_TERM ret;
  ErlNifBinary m, ad, out;
  ErlNifUInt64 tag;
  enacl_secretstream_ctx *obj = NULL;

  if (argc != 4) {
    goto bad_arg;
  }

  if (!enif_get_resource(env, argv[0],
                         (ErlNifResourceType *)enacl_secretstream_ctx_rtype,
                         (void **)&obj)) {
    goto bad_arg;
  }

  if (!enif_inspect_binary(env, argv[1], &m)) {
    goto bad_arg;
  }

  if (!enif_inspect_binary(env, argv[2], &ad)) {
    goto bad_arg;
  }

  if (!enif_get_uint64(env, argv[3], &tag)) {
    goto bad_arg;
  }

  if (!enif_alloc_binary(m.size + crypto_secretstream_xchacha20poly1305_ABYTES,
                         &out)) {
    return enacl_internal_error(env);
  }

  enif_mutex_lock(obj->mtx);
  if (!obj->alive) {
    goto err;
  }

  crypto_secretstream_xchacha20poly1305_push(obj->state, out.data, NULL, m.data,
                                             m.size, ad.data, ad.size, tag);

  if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL) {
    if (obj->state) {
      obj->alive = 0;
      sodium_memzero(obj->state,
                     crypto_secretstream_xchacha20poly1305_statebytes());
      enif_free(obj->state);
      obj->state = NULL;
    }
  }

  ret = enif_make_binary(env, &out);

  goto done;

bad_arg:
  return enif_make_badarg(env);
err:
  ret = enacl_error_finalized(env);
  enif_release_binary(&out);
done:
  enif_mutex_unlock(obj->mtx);
  return ret;
}

/*
   crypto_secretstream_xchacha20poly1305_pull
   (crypto_secretstream_xchacha20poly1305_state *state,
   unsigned char *m, unsigned long long *mlen_p, unsigned char *tag_p,
   const unsigned char *in, unsigned long long inlen,
   const unsigned char *ad, unsigned long long adlen)
   */
ERL_NIF_TERM
enacl_crypto_secretstream_xchacha20poly1305_pull(ErlNifEnv *env, int argc,
                                                 const ERL_NIF_TERM argv[]) {
  ERL_NIF_TERM ret;
  ErlNifBinary m, in, ad;
  unsigned char tag;
  enacl_secretstream_ctx *obj = NULL;

  if (argc != 3) {
    goto bad_arg;
  }

  if (!enif_get_resource(env, argv[0],
                         (ErlNifResourceType *)enacl_secretstream_ctx_rtype,
                         (void **)&obj)) {
    goto bad_arg;
  }

  if (!enif_inspect_binary(env, argv[1], &in)) {
    goto bad_arg;
  }

  if (in.size < crypto_secretstream_xchacha20poly1305_ABYTES) {
    goto bad_arg;
  }

  if (!enif_inspect_binary(env, argv[2], &ad)) {
    goto bad_arg;
  }

  if (in.size < crypto_secretstream_xchacha20poly1305_ABYTES) {
    goto bad_arg;
  }

  if (!enif_alloc_binary(in.size - crypto_secretstream_xchacha20poly1305_ABYTES,
                         &m)) {
    return enacl_internal_error(env);
  }

  enif_mutex_lock(obj->mtx);
  if (!obj->alive) {
    goto err;
  }

  if (0 != crypto_secretstream_xchacha20poly1305_pull(obj->state, m.data, NULL,
                                                      &tag, in.data, in.size,
                                                      ad.data, ad.size)) {
    ret = enacl_error_tuple(env, "failed_verification");
    goto release;
  }

  if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL) {
    if (obj->state) {
      obj->alive = 0;
      sodium_memzero(obj->state,
                     crypto_secretstream_xchacha20poly1305_statebytes());
      enif_free(obj->state);
      obj->state = NULL;
    }
  }

  ret = enif_make_tuple2(env, enif_make_binary(env, &m),
                         enif_make_int64(env, tag));

  goto done;

bad_arg:
  return enif_make_badarg(env);
err:
  ret = enacl_error_finalized(env);
release:
  enif_release_binary(&m);
done:
  enif_mutex_unlock(obj->mtx);
  return ret;
}


================================================
FILE: c_src/secretstream.h
================================================
#ifndef ENACL_SECRETSTREAM_H
#define ENACL_SECRETSTREAM_H

#include <erl_nif.h>

int enacl_init_secretstream_ctx(ErlNifEnv *env);

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_ABYTES(
    ErlNifEnv *env, int argc,
    const ERL_NIF_TERM argv[]
    );

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_HEADERBYTES(
    ErlNifEnv *env, int argc,
    const ERL_NIF_TERM argv[]
    );

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_KEYBYTES(
    ErlNifEnv *env, int argc,
    const ERL_NIF_TERM argv[]
    );

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX(
    ErlNifEnv *env, int argc,
    const ERL_NIF_TERM argv[]
    );

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_MESSAGE(
    ErlNifEnv *env, int argc,
    const ERL_NIF_TERM argv[]
    );

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_PUSH(
    ErlNifEnv *env, int argc,
    const ERL_NIF_TERM argv[]
    );

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_REKEY(
    ErlNifEnv *env, int argc,
    const ERL_NIF_TERM argv[]
    );

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_FINAL(
    ErlNifEnv *env, int argc,
    const ERL_NIF_TERM argv[]
    );

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_keygen(
    ErlNifEnv *env, int argc,
    const ERL_NIF_TERM argv[]
    );

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_init_push(
    ErlNifEnv *env, int argc,
    const ERL_NIF_TERM argv[]
    );

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_init_pull(
    ErlNifEnv *env, int argc,
    const ERL_NIF_TERM argv[]
    );

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_rekey(
    ErlNifEnv *env, int argc,
    const ERL_NIF_TERM argv[]
    );

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_push(
    ErlNifEnv *env, int argc,
    const ERL_NIF_TERM argv[]
    );

ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_pull(
    ErlNifEnv *env, int argc,
    const ERL_NIF_TERM argv[]
    );

#endif


================================================
FILE: c_src/sign.c
================================================
#include <sodium.h>

#include <erl_nif.h>

#include "enacl.h"
#include "sign.h"

typedef struct enacl_sign_ctx {
  ErlNifMutex *mtx;
  crypto_sign_state *state; // The underlying signature state
  int alive; // Is the context still valid for updates/finalization
} enacl_sign_ctx;

ErlNifResourceType *enacl_sign_ctx_rtype = NULL;

void enacl_sign_ctx_dtor(ErlNifEnv *env, enacl_sign_ctx *);

int enacl_init_sign_ctx(ErlNifEnv *env) {
  enacl_sign_ctx_rtype =
      enif_open_resource_type(env, NULL, "enacl_sign_context",
                              (ErlNifResourceDtor *)enacl_sign_ctx_dtor,
                              ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER, NULL);

  if (enacl_sign_ctx_rtype == NULL)
    return 0;

  return 1;
}

void enacl_sign_ctx_dtor(ErlNifEnv *env, enacl_sign_ctx *obj) {
  if (!obj->alive)
    return;

  if (obj->state) {
    sodium_memzero(obj->state, crypto_sign_statebytes());
    enif_free(obj->state);
  }

  if (obj->mtx != NULL)
    enif_mutex_destroy(obj->mtx);

  return;
}

/*
  int crypto_sign_init(crypto_sign_state *state)
 */

ERL_NIF_TERM enacl_crypto_sign_init(ErlNifEnv *env, int argc,
                                    ERL_NIF_TERM const argv[]) {
  ERL_NIF_TERM ret;
  enacl_sign_ctx *obj = NULL;

  if (argc != 0)
    goto bad_arg;

  if ((obj = enif_alloc_resource(enacl_sign_ctx_rtype,
                                 sizeof(enacl_sign_ctx))) == NULL) {
    ret = enacl_internal_error(env);
    goto done;
  }
  obj->alive = 0;
  obj->state = enif_alloc(crypto_sign_statebytes());
  if (obj->state == NULL) {
    goto release;
  }
  obj->alive = 1;

  if ((obj->mtx = enif_mutex_create("enacl.sign")) == NULL) {
    goto free;
  }

  if (0 != crypto_sign_init(obj->state)) {
    goto free;
  }

  // Create return values
  ret = enif_make_resource(env, obj);

  goto release;

bad_arg:
  return enif_make_badarg(env);
free:
  if (obj->alive)
    if (obj->state != NULL) {
      sodium_memzero(obj->state, crypto_sign_statebytes());
      enif_free(obj->state);
      obj->state = NULL;
    }
release:
  // This also frees the mutex via the destructor
  enif_release_resource(obj);
done:
  return ret;
}

/*
  int crypto_sign_update(crypto_sign_state *state,
                        const unsigned char *m,
                        unsigned long long mlen);
 */

ERL_NIF_TERM enacl_crypto_sign_update(ErlNifEnv *env, int argc,
                                      ERL_NIF_TERM const argv[]) {
  ERL_NIF_TERM ret;
  enacl_sign_ctx *obj = NULL;
  ErlNifBinary data;

  // Validate the arguments
  if (argc != 2)
    goto bad_arg;
  if (!enif_get_resource(env, argv[0], enacl_sign_ctx_rtype, (void **)&obj))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[1], &data))
    goto bad_arg;

  enif_mutex_lock(obj->mtx);
  if (!obj->alive) {
    ret = enacl_error_finalized(env);
    goto done;
  }

  if (0 != crypto_sign_update(obj->state, data.data, data.size)) {
    ret = enacl_internal_error(env); // This should never be hit
    goto done;
  }

  ret = argv[0];
  goto done;

bad_arg:
  return enif_make_badarg(env);
done:
  enif_mutex_unlock(obj->mtx);
  return ret;
}

ERL_NIF_TERM enacl_crypto_sign_final_create(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]) {
  ERL_NIF_TERM ret;
  enacl_sign_ctx *obj = NULL;
  ErlNifBinary sk, sig;
  unsigned long long siglen;

  if (argc != 2)
    goto bad_arg;
  if (!enif_get_resource(env, argv[0], enacl_sign_ctx_rtype, (void **)&obj))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[1], &sk))
    goto bad_arg;
  if (sk.size != crypto_sign_SECRETKEYBYTES)
    goto bad_arg;

  enif_mutex_lock(obj->mtx);
  if (!obj->alive) {
    ret = enacl_error_finalized(env);
    goto done;
  }

  if (!enif_alloc_binary(crypto_sign_BYTES, &sig)) {
    ret = enacl_internal_error(env);
    goto done;
  }

  crypto_sign_final_create(obj->state, sig.data, &siglen, sk.data);

  ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK);
  ERL_NIF_TERM signature = enif_make_binary(env, &sig);

  ret = enif_make_tuple2(env, ok, signature);
  goto cleanup;

bad_arg:
  return enif_make_badarg(env);
cleanup:
  obj->alive = 0;
  sodium_memzero(obj->state, crypto_sign_statebytes());
  enif_free(obj->state);
  obj->state = NULL;
done:
  enif_mutex_unlock(obj->mtx);
  return ret;
}

ERL_NIF_TERM enacl_crypto_sign_final_verify(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]) {
  ErlNifBinary pk, sig;
  enacl_sign_ctx *obj = NULL;
  ERL_NIF_TERM ret;

  if (argc != 3)
    goto bad_arg;
  if (!enif_get_resource(env, argv[0], enacl_sign_ctx_rtype, (void **)&obj))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[1], &sig))
    goto bad_arg;
  if (!enif_inspect_binary(env, argv[2], &pk))
    goto bad_arg;
  if (pk.size != crypto_sign_PUBLICKEYBYTES)
    goto bad_arg;

  enif_mutex_lock(obj->mtx);
  if (!obj->alive) {
    ret = enacl_error_finalized(env);
    goto done;
  }

  if (0 == crypto_sign_final_verify(obj->state, sig.data, pk.data)) {
    ret = enif_make_atom(env, "true");
  } else {
    ret = enif_make_atom(env, "false");
  }
  // Mark as done
  goto cleanup;

bad_arg:
  return enif_make_badarg(env);
cleanup:
  // Get rid of the context and mark it as dead
  obj->alive = 0;
  sodium_memzero(obj->state, crypto_sign_statebytes());
  enif_free(obj->state);
  obj->state = NULL;
done:
  enif_mutex_unlock(obj->mtx);
  return ret;
}

/* Ed 25519 */
ERL_NIF_TERM
enacl_crypto_sign_ed25519_keypair(ErlNifEnv *env, int argc,
                                  ERL_NIF_TERM const argv[]) {
  ErlNifBinary pk, sk;

  if (argc != 0) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(crypto_sign_ed25519_PUBLICKEYBYTES, &pk)) {
    return enacl_internal_error(env);
  }

  if (!enif_alloc_binary(crypto_sign_ed25519_SECRETKEYBYTES, &sk)) {
    enif_release_binary(&pk);
    return enacl_internal_error(env);
  }

  crypto_sign_ed25519_keypair(pk.data, sk.data);

  return enif_make_tuple2(env, enif_make_binary(env, &pk),
                          enif_make_binary(env, &sk));
}

ERL_NIF_TERM
enacl_crypto_sign_ed25519_sk_to_pk(ErlNifEnv *env, int argc,
                                   ERL_NIF_TERM const argv[]) {
  ErlNifBinary pk, sk;

  if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &sk)) ||
      (sk.size != crypto_sign_ed25519_SECRETKEYBYTES)) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(crypto_sign_ed25519_PUBLICKEYBYTES, &pk)) {
    return enacl_internal_error(env);
  }

  crypto_sign_ed25519_sk_to_pk(pk.data, sk.data);

  return enif_make_binary(env, &pk);
}

ERL_NIF_TERM
enacl_crypto_sign_ed25519_public_to_curve25519(ErlNifEnv *env, int argc,
                                               ERL_NIF_TERM const argv[]) {
  ErlNifBinary curve25519_pk, ed25519_pk;

  if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &ed25519_pk)) ||
      (ed25519_pk.size != crypto_sign_ed25519_PUBLICKEYBYTES)) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &curve25519_pk)) {
    return enacl_internal_error(env);
  }

  crypto_sign_ed25519_pk_to_curve25519(curve25519_pk.data, ed25519_pk.data);

  return enif_make_binary(env, &curve25519_pk);
}

ERL_NIF_TERM
enacl_crypto_sign_ed25519_secret_to_curve25519(ErlNifEnv *env, int argc,
                                               ERL_NIF_TERM const argv[]) {
  ErlNifBinary curve25519_sk, ed25519_sk;

  if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &ed25519_sk)) ||
      (ed25519_sk.size != crypto_sign_ed25519_SECRETKEYBYTES)) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &curve25519_sk)) {
    return enacl_internal_error(env);
  }

  crypto_sign_ed25519_sk_to_curve25519(curve25519_sk.data, ed25519_sk.data);

  return enif_make_binary(env, &curve25519_sk);
}

ERL_NIF_TERM
enacl_crypto_sign_ed25519_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
                                         ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_sign_ed25519_PUBLICKEYBYTES);
}

ERL_NIF_TERM
enacl_crypto_sign_ed25519_SECRETKEYBYTES(ErlNifEnv *env, int argc,
                                         ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_sign_ed25519_SECRETKEYBYTES);
}

ERL_NIF_TERM enacl_crypto_sign_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
                                              ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_sign_PUBLICKEYBYTES);
}

ERL_NIF_TERM enacl_crypto_sign_SECRETKEYBYTES(ErlNifEnv *env, int argc,
                                              ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_sign_SECRETKEYBYTES);
}

ERL_NIF_TERM enacl_crypto_sign_SEEDBYTES(ErlNifEnv *env, int argc,
                                         ERL_NIF_TERM const argv[]) {
  return enif_make_int64(env, crypto_sign_SEEDBYTES);
}

ERL_NIF_TERM enacl_crypto_sign_keypair(ErlNifEnv *env, int argc,
                                       ERL_NIF_TERM const argv[]) {
  ErlNifBinary pk, sk;

  if (argc != 0) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(crypto_sign_PUBLICKEYBYTES, &pk)) {
    return enacl_internal_error(env);
  }

  if (!enif_alloc_binary(crypto_sign_SECRETKEYBYTES, &sk)) {
    enif_release_binary(&pk);
    return enacl_internal_error(env);
  }

  crypto_sign_keypair(pk.data, sk.data);

  return enif_make_tuple2(env, enif_make_binary(env, &pk),
                          enif_make_binary(env, &sk));
}

ERL_NIF_TERM enacl_crypto_sign_seed_keypair(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]) {
  ErlNifBinary pk, sk, seed;

  if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &seed))) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(crypto_sign_PUBLICKEYBYTES, &pk)) {
    return enacl_internal_error(env);
  }

  if (!enif_alloc_binary(crypto_sign_SECRETKEYBYTES, &sk)) {
    enif_release_binary(&pk);
    return enacl_internal_error(env);
  }

  crypto_sign_seed_keypair(pk.data, sk.data, seed.data);

  return enif_make_tuple2(env, enif_make_binary(env, &pk),
                          enif_make_binary(env, &sk));
}

ERL_NIF_TERM enacl_crypto_sign(ErlNifEnv *env, int argc,
                               ERL_NIF_TERM const argv[]) {
  ErlNifBinary m, sk, sm;
  unsigned long long smlen;

  if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
      (!enif_inspect_binary(env, argv[1], &sk))) {
    return enif_make_badarg(env);
  }

  if (sk.size != crypto_sign_SECRETKEYBYTES) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(m.size + crypto_sign_BYTES, &sm)) {
    return enacl_internal_error(env);
  }

  crypto_sign(sm.data, &smlen, m.data, m.size, sk.data);

  return enif_make_sub_binary(env, enif_make_binary(env, &sm), 0, smlen);
}

ERL_NIF_TERM enacl_crypto_sign_open(ErlNifEnv *env, int argc,
                                    ERL_NIF_TERM const argv[]) {
  ErlNifBinary m, sm, pk;
  unsigned long long mlen;

  if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &sm)) ||
      (!enif_inspect_binary(env, argv[1], &pk))) {
    return enif_make_badarg(env);
  }

  if (pk.size != crypto_sign_PUBLICKEYBYTES) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(sm.size, &m)) {
    return enacl_internal_error(env);
  }

  if (0 == crypto_sign_open(m.data, &mlen, sm.data, sm.size, pk.data)) {
    ERL_NIF_TERM ret_ok = enif_make_atom(env, ATOM_OK);
    ERL_NIF_TERM ret_bin =
        enif_make_sub_binary(env, enif_make_binary(env, &m), 0, mlen);
    return enif_make_tuple2(env, ret_ok, ret_bin);
  } else {
    enif_release_binary(&m);
    return enacl_error_tuple(env, "failed_verification");
  }
}

ERL_NIF_TERM enacl_crypto_sign_detached(ErlNifEnv *env, int argc,
                                        ERL_NIF_TERM const argv[]) {
  ErlNifBinary m, sk, sig;
  unsigned long long siglen;

  if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
      (!enif_inspect_binary(env, argv[1], &sk))) {
    return enif_make_badarg(env);
  }

  if (sk.size != crypto_sign_SECRETKEYBYTES) {
    return enif_make_badarg(env);
  }

  if (!enif_alloc_binary(crypto_sign_BYTES, &sig)) {
    return enacl_internal_error(env);
  }

  crypto_sign_detached(sig.data, &siglen, m.data, m.size, sk.data);

  return enif_make_binary(env, &sig);
}

ERL_NIF_TERM
enacl_crypto_sign_verify_detached(ErlNifEnv *env, int argc,
                                  ERL_NIF_TERM const argv[]) {
  ErlNifBinary m, sig, pk;

  if ((argc != 3) || (!enif_inspect_binary(env, argv[0], &sig)) ||
      (!enif_inspect_iolist_as_binary(env, argv[1], &m)) ||
      (!enif_inspect_binary(env, argv[2], &pk))) {
    return enif_make_badarg(env);
  }

  if (sig.size != crypto_sign_BYTES) {
    return enif_make_badarg(env);
  }

  if (pk.size != crypto_sign_PUBLICKEYBYTES) {
    return enif_make_badarg(env);
  }

  if (0 == crypto_sign_verify_detached(sig.data, m.data, m.size, pk.data)) {
    return enif_make_atom(env, ATOM_TRUE);
  } else {
    return enif_make_atom(env, ATOM_FALSE);
  }
}


================================================
FILE: c_src/sign.h
================================================
#ifndef ENACL_SIGN_H
#define ENACL_SIGN_H

#include <erl_nif.h>

int enacl_init_sign_ctx(ErlNifEnv *env);

ERL_NIF_TERM enacl_crypto_sign_init(ErlNifEnv *env, int argc,
                                    ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_sign_update(ErlNifEnv *env, int argc,
                                      ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_sign_final_create(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_sign_final_verify(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_sign_ed25519_keypair(ErlNifEnv *env, int argc,
                                               ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_sign_ed25519_sk_to_pk(ErlNifEnv *env, int argc,
                                                ERL_NIF_TERM const argv[]);

ERL_NIF_TERM
enacl_crypto_sign_ed25519_public_to_curve25519(ErlNifEnv *env, int argc,
                                               ERL_NIF_TERM const argv[]);

ERL_NIF_TERM
enacl_crypto_sign_ed25519_secret_to_curve25519(ErlNifEnv *env, int argc,
                                               ERL_NIF_TERM const argv[]);

ERL_NIF_TERM
enacl_crypto_sign_ed25519_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
                                         ERL_NIF_TERM const argv[]);

ERL_NIF_TERM
enacl_crypto_sign_ed25519_SECRETKEYBYTES(ErlNifEnv *env, int argc,
                                         ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_sign_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
                                              ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_sign_SECRETKEYBYTES(ErlNifEnv *env, int argc,
                                              ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_sign_SEEDBYTES(ErlNifEnv *env, int argc,
                                         ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_sign_keypair(ErlNifEnv *env, int argc,
                                       ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_sign_seed_keypair(ErlNifEnv *env, int argc,
                                            ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_sign(ErlNifEnv *env, int argc,
                               ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_sign_open(ErlNifEnv *env, int argc,
                                    ERL_NIF_TERM const argv[]);

ERL_NIF_TERM enacl_crypto_sign_detached(ErlNifEnv *env, int argc,
                                        ERL_NIF_TERM const argv[]);

ERL_NIF_TERM
enacl_crypto_sign_verify_detached(ErlNifEnv *env, int argc,
                                  ERL_NIF_TERM const argv[]);

#endif

================================================
FILE: doc/overview.edoc
================================================
@author Jesper Louis Andersen

@title  enacl - Erlang bindings for the NaCl cryptographic library



================================================
FILE: eqc_test/Makefile
================================================
eqc_compile:
	$(MAKE) -C .. eqc_compile

console:
	erl -pa ../ebin ../deps/*/ebin

clean:
	rm *.beam


================================================
FILE: eqc_test/enacl_eqc.erl
================================================
-module(enacl_eqc).
-include_lib("eqc/include/eqc.hrl").
-compile([export_all, nowarn_export_all]).

-ifndef(mini).
-compile({parse_transform, eqc_parallelize}).
-define(FAULT(Arg1, Arg2), fault(Arg1, Arg2)).
-define(FAULT_RATE(Arg1, Arg2, Arg3), fault_rate(Arg1, Arg2, Arg3)).
-else.
-define(FAULT(Arg1, Arg2), noop_fault(Arg1, Arg2)).
-define(FAULT_RATE(Arg1, Arg2, Arg3), noop_fault_rate(Arg1, Arg2, Arg3)).
-endif.

start()->
  eqc:module(?MODULE).

noop_fault(_Bad, Good) -> Good.

noop_fault_rate(_1, _2, Gen) -> Gen.

non_byte_int() ->
    oneof([
        ?LET(N, nat(), -(N+1)),
        ?LET(N, nat(), N+256)
    ]).

g_iolist() ->
    ?SIZED(Sz, g_iolist(Sz)).

g_iolist(0) ->
    ?FAULT(
        oneof([
          elements([a,b,c]),
          real(),
          non_byte_int()
        ]),
        return([]));
g_iolist(N) ->
    ?FAULT(
        oneof([
          elements([a,b,c]),
          real(),
          non_byte_int()
        ]),
        frequency([
            {1, g_iolist(0)},
            {N, ?LAZY(list(oneof([char(), binary(), g_iolist(N div 4)])))}
        ])).

g_iodata() ->
    ?FAULT(
      oneof([elements([a,b,c]), real()]),
      oneof([binary(), g_iolist(), eqc_gen:largebinary(64*1024)])).

v_iolist([]) -> true;
v_iolist([B|Xs]) when is_binary(B) -> v_iolist(Xs);
v_iolist([C|Xs]) when is_integer(C), C >= 0, C < 256 -> v_iolist(Xs);
v_iolist([L|Xs]) when is_list(L) ->
    v_iolist(L) andalso v_iolist(Xs);
v_iolist(_) -> false.

v_iodata(B) when is_binary(B) -> true;
v_iodata(Structure) -> v_iolist(Structure).

%% Generator for binaries of a given size with different properties and fault injection:
g_binary(Sz) ->
    ?FAULT(g_binary_bad(Sz), g_binary_good(Sz)).

g_binary_good(Sz) when Sz =< 32 -> binary(Sz);
g_binary_good(Sz) -> eqc_gen:largebinary(Sz).

g_binary_bad(Sz) ->
    frequency([
        {5, ?SUCHTHAT(B, binary(), byte_size(B) /= Sz)},
        {1, elements([a, b])},
        {1, int()},
        {1, g_iodata()}
    ]).

v_binary(Sz, N) when is_binary(N) ->
    byte_size(N) == Sz;
v_binary(_, _) -> false.


%% Typical generators based on the binaries
nonce() -> g_binary(enacl:box_NONCEBYTES()).
nonce_valid(N) -> v_binary(enacl:box_NONCEBYTES(), N).

%% Generator of natural numbers
g_nat() ->
    ?FAULT(g_nat_bad(), nat()).

g_nat_bad() ->
    oneof([
        elements([a,b,c]),
        real(),
        binary(),
        ?LET(X, nat(), -X)
    ]).

is_nat(N) when is_integer(N), N >= 0 -> true;
is_nat(_) -> false.

keypair_good() ->
    #{ public := PK, secret := SK} = enacl:box_keypair(),
    {PK, SK}.

keypair_bad() ->
    ?LET(X, elements([pk, sk]),
      begin
        #{ public := PK, secret := SK} = enacl:box_keypair(),
        case X of
            pk ->
              PKBytes = enacl:box_PUBLICKEYBYTES(),
              {oneof([return(a), nat(), ?SUCHTHAT(B, binary(), byte_size(B) /= PKBytes)]), SK};
            sk ->
              SKBytes = enacl:box_SECRETKEYBYTES(),
              {PK, oneof([return(a), nat(), ?SUCHTHAT(B, binary(), byte_size(B) /= SKBytes)])}
        end
      end).

keypair() ->
    ?FAULT(keypair_bad(), keypair_good()).

kx_keypair_good() ->
  #{ public := PK, secret := SK} = enacl:kx_keypair(),
  {PK, SK}.

kx_keypair_bad() ->
  ?LET(X, elements([pk, sk]),
  begin
    #{ public := PK, secret := SK} = enacl:box_keypair(),
    case X of
      pk ->
        PKBytes = enacl:kx_public_key_size(),
        {oneof([return(a), nat(), ?SUCHTHAT(B, binary(), byte_size(B) /= PKBytes)]), SK};
      sk ->
        SKBytes = enacl:kx_secret_key_size(),
        {PK, oneof([return(a), nat(), ?SUCHTHAT(B, binary(), byte_size(B) /= SKBytes)])}
    end
  end).

g_generichash_data() ->
  binary().

g_generichash_key() ->
  ?LET({Min, Max}, {return(enacl_nif:crypto_generichash_KEYBYTES_MIN()), return(enacl_nif:crypto_generichash_KEYBYTES_MAX())},
    largebinary({limit, Min, Max})).

g_generichash_size() ->
  ?LET({Min, Max}, {return(enacl_nif:crypto_generichash_BYTES_MIN()), return(enacl_nif:crypto_generichash_BYTES_MAX())},
    choose(Min, Max)).

%% CRYPTO BOX
%% ---------------------------
%% * box/4
%% * box_open/4
%% * box_beforenm/2
%% * box_afternm/3
%% * box_open_afternm/3
keypair_valid(PK, SK) when is_binary(PK), is_binary(SK) ->
    PKBytes = enacl:box_PUBLICKEYBYTES(),
    SKBytes = enacl:box_SECRETKEYBYTES(),
    byte_size(PK) == PKBytes andalso byte_size(SK) == SKBytes;
keypair_valid(_PK, _SK) -> false.

prop_box_keypair() ->
    ?FORALL(_X, return(dummy),
        ok_box_keypair(enacl:box_keypair())).

ok_box_keypair(#{ public := _, secret := _}) -> true;
ok_box_keypair(_) -> false.

box(Msg, Nonce , PK, SK) ->
    try
        enacl:box(Msg, Nonce, PK, SK)
    catch
        error:badarg -> badarg
    end.

box_seal(Msg, PK) ->
    try
        enacl:box_seal(Msg, PK)
    catch
       error:badarg -> badarg
    end.

box_seal_open(Cph, PK, SK) ->
    try
        enacl:box_seal_open(Cph, PK, SK)
    catch
        error:badarg -> badarg
    end.

box_open(CphText, Nonce, PK, SK) ->
    try
        enacl:box_open(CphText, Nonce, PK, SK)
    catch
         error:badarg -> badarg
    end.

failure(badarg) -> true;
failure({error, failed_verification}) -> true;
failure(X) -> {failure, X}.

prop_box_correct() ->
    ?FORALL({Msg, Nonce, {PK1, SK1}, {PK2, SK2}},
            {?FAULT_RATE(1, 40, g_iodata()),
             ?FAULT_RATE(1, 40, nonce()),
             ?FAULT_RATE(1, 40, keypair()),
             ?FAULT_RATE(1, 40, keypair())},
        begin
            case v_iodata(Msg) andalso nonce_valid(Nonce) andalso keypair_valid(PK1, SK1) andalso keypair_valid(PK2, SK2) of
                true ->
                    Key = enacl:box_beforenm(PK2, SK1),
                    Key = enacl:box_beforenm(PK1, SK2),
                    CipherText = enacl:box(Msg, Nonce, PK2, SK1),
                    CipherText = enacl:box_afternm(Msg, Nonce, Key),
                    {ok, DecodedMsg} = enacl:box_open(CipherText, Nonce, PK1, SK2),
                    {ok, DecodedMsg} = enacl:box_open_afternm(CipherText, Nonce, Key),
                    equals(iolist_to_binary(Msg), DecodedMsg);
                false ->
                    case box(Msg, Nonce, PK2, SK1) of
                        badarg -> true;
                        Res -> failure(box_open(Res, Nonce, PK1, SK2))
                    end
            end
        end).

prop_box_failure_integrity() ->
    ?FORALL({Msg, Nonce, {PK1, SK1}, {PK2, SK2}},
            {?FAULT_RATE(1, 40, g_iodata()),
             ?FAULT_RATE(1, 40, nonce()),
             ?FAULT_RATE(1, 40, keypair()),
             ?FAULT_RATE(1, 40, keypair())},
        begin
            case v_iodata(Msg)
                 andalso nonce_valid(Nonce)
                 andalso keypair_valid(PK1, SK1)
                 andalso keypair_valid(PK2, SK2) of
                true ->
                    Key = enacl:box_beforenm(PK2, SK1),
                    CipherText = enacl:box(Msg, Nonce, PK2, SK1),
                    Err = enacl:box_open([<<"x">>, CipherText], Nonce, PK1, SK2),
                    Err = enacl:box_open_afternm([<<"x">>, CipherText], Nonce, Key),
                    equals(Err, {error, failed_verification});
                false ->
                    case box(Msg, Nonce, PK2, SK1) of
                      badarg -> true;
                      Res ->
                        failure(box_open(Res, Nonce, PK1, SK2))
                    end
            end
        end).


%% PRECOMPUTATIONS
beforenm_key() ->
    ?LET([{PK1, SK1}, {PK2, SK2}], [?FAULT_RATE(1, 40, keypair()), ?FAULT_RATE(1, 40, keypair())],
        case keypair_valid(PK1, SK1) andalso keypair_valid(PK2, SK2) of
            true ->
                enacl:box_beforenm(PK1, SK2);
            false ->
                oneof([
                  elements([a,b,c]),
                  real(),
                  ?SUCHTHAT(X, binary(), byte_size(X) /= enacl:box_BEFORENMBYTES())
                  ])
        end).

v_key(K) when is_binary(K) -> byte_size(K) == enacl:box_BEFORENMBYTES();
v_key(_) -> false.

prop_beforenm_correct() ->
    ?FORALL([{PK1, SK1}, {PK2, SK2}], [?FAULT_RATE(1, 40, keypair()), ?FAULT_RATE(1, 40, keypair())],
        case keypair_valid(PK1, SK1) andalso keypair_valid(PK2, SK2) of
            true ->
                equals(enacl:box_beforenm(PK1, SK2), enacl:box_beforenm(PK2, SK1));
            false ->
                badargs(fun() ->
                	K = enacl:box_beforenm(PK1, SK2),
                	K = enacl:box_beforenm(PK2, SK1)
                end)
        end).

prop_afternm_correct() ->
    ?FORALL([Msg, Nonce, Key],
        [?FAULT_RATE(1, 40, g_iodata()),
         ?FAULT_RATE(1, 40, nonce()),
         ?FAULT_RATE(1, 40, beforenm_key())],
      begin
          case v_iodata(Msg) andalso nonce_valid(Nonce) andalso v_key(Key) of
              true ->
                  CipherText = enacl:box_afternm(Msg, Nonce, Key),
                  equals({ok, iolist_to_binary(Msg)}, enacl:box_open_afternm(CipherText, Nonce, Key));
              false ->
                  try enacl:box_afternm(Msg, Nonce, Key) of
                      CipherText ->
                          try enacl:box_open_afternm(CipherText, Nonce, Key) of
                              {ok, _M} -> false;
                              {error, failed_validation} -> false
                          catch
                              error:badarg -> true
                          end
                  catch
                      error:badarg -> true
                  end
          end
      end).

%% SIGNATURES
%% ----------

prop_sign_keypair() ->
    ?FORALL(_D, return(dummy),
      begin
        #{ public := _, secret := _ } = enacl:sign_keypair(),
        true
      end).

sign_keypair_bad() ->
  ?LET(X, elements([pk, sk]),
    begin
      KP = enacl:sign_keypair(),
      case X of
        pk ->
          Sz = enacl:sign_PUBLICBYTES(),
          ?LET(Wrong, oneof([a, int(), ?SUCHTHAT(B, binary(), byte_size(B) /= Sz)]),
            KP#{ public := Wrong });
        sk ->
          Sz = enacl:sign_SECRETBYTES(),
          ?LET(Wrong, oneof([a, int(), ?SUCHTHAT(B, binary(), byte_size(B) /= Sz)]),
            KP#{ secret := Wrong })
      end
    end).

sign_keypair_good() ->
  return(enacl:sign_keypair()).

sign_keypair() ->
  ?FAULT(sign_keypair_bad(), sign_keypair_good()).

sign_keypair_public_valid(#{ public := Public })
  when is_binary(Public) ->
    byte_size(Public) == enacl:sign_PUBLICBYTES();
sign_keypair_public_valid(_) -> false.

sign_keypair_secret_valid(#{ secret := Secret })
  when is_binary(Secret) ->
    byte_size(Secret) == enacl:sign_SECRETBYTES();
sign_keypair_secret_valid(_) -> false.

sign_keypair_valid(KP) ->
  sign_keypair_public_valid(KP) andalso sign_keypair_secret_valid(KP).

prop_sign_detached() ->
    ?FORALL({Msg, KeyPair},
        {?FAULT_RATE(1, 40, g_iodata()),
         ?FAULT_RATE(1, 40, sign_keypair())},
      begin
          case v_iodata(Msg) andalso sign_keypair_secret_valid(KeyPair) of
            true ->
                #{ secret := Secret } = KeyPair,
                enacl:sign_detached(Msg, Secret),
                true;
            false ->
                #{ secret := Secret } = KeyPair,
                badargs(fun() -> enacl:sign_detached(Msg, Secret) end)
          end
      end).

prop_sign() ->
    ?FORALL({Msg, KeyPair},
          {?FAULT_RATE(1, 40, g_iodata()),
           ?FAULT_RATE(1, 40, sign_keypair())},
      begin
        case v_iodata(Msg) andalso sign_keypair_secret_valid(KeyPair) of
          true ->
            #{ secret := Secret } = KeyPair,
            enacl:sign(Msg, Secret),
            true;
          false ->
            #{ secret := Secret } = KeyPair,
            badargs(fun() -> enacl:sign(Msg, Secret) end)
        end
      end).

signed_message_good(M) ->
    #{ public := PK, secret := SK} = enacl:sign_keypair(),
    SM = enacl:sign(M, SK),
    frequency([
        {3, return({{valid, SM}, PK})},
        {1, ?LET(X, elements([sm, pk]),
               case X of
                 sm -> {{invalid, binary(byte_size(SM))}, PK};
                 pk -> {{invalid, SM}, binary(byte_size(PK))}
               end)}]).

signed_message_good_d(M) ->
    #{ public := PK, secret := SK} = enacl:sign_keypair(),
    Sig = enacl:sign_detached(M, SK),
    frequency([
        {3, return({{valid, Sig}, PK})},
        {1, ?LET(X, elements([sm, pk]),
               case X of
                 sm -> {{invalid, binary(byte_size(Sig))}, PK};
                 pk -> {{invalid, Sig}, binary(byte_size(PK))}
               end)}]).

signed_message_bad() ->
    Sz = enacl:sign_PUBLICBYTES(),
    {binary(), oneof([a, int(), ?SUCHTHAT(B, binary(Sz), byte_size(B) /= Sz)])}.

signed_message_bad_d() ->
    Sz = enacl:sign_PUBLICBYTES(),
    {binary(), oneof([a, int(), ?SUCHTHAT(B, binary(Sz), byte_size(B) /= Sz)])}.

signed_message(M) ->
    ?FAULT(signed_message_bad(), signed_message_good(M)).

signed_message_d(M) ->
    ?FAULT(signed_message_bad_d(), signed_message_good_d(M)).

signed_message_valid({valid, _}, _) -> true;
signed_message_valid({invalid, _}, _) -> true;
signed_message_valid(_, _) -> false.

prop_sign_detached_open() ->
    ?FORALL(Msg, g_iodata(),
      ?FORALL({SignMsg, PK}, signed_message_d(Msg),
          case v_iodata(Msg) andalso signed_message_valid(SignMsg, PK) of
              true ->
                  case SignMsg of
                    {valid, Sig} ->
                        equals(true, enacl:sign_verify_detached(Sig, Msg, PK));
                    {invalid, Sig} ->
                        equals(false, enacl:sign_verify_detached(Sig, Msg, PK))
                  end;
              false ->
                  badargs(fun() -> enacl:sign_verify_detached(SignMsg, Msg, PK) end)
          end)).

prop_sign_open() ->
    ?FORALL(Msg, g_iodata(),
      ?FORALL({SignMsg, PK}, signed_message(Msg),
          case v_iodata(Msg) andalso signed_message_valid(SignMsg, PK) of
              true ->
                  case SignMsg of
                    {valid, SM} ->
                        equals({ok, iolist_to_binary(Msg)}, enacl:sign_open(SM, PK));
                    {invalid, SM} ->
                        equals({error, failed_verification}, enacl:sign_open(SM, PK))
                  end;
              false ->
                  badargs(fun() -> enacl:sign_open(SignMsg, PK) end)
          end)).

prop_seal_box_failure_integrity() ->
    ?FORALL({Msg, {PK1, SK1}}, {?FAULT_RATE(1,40,g_iodata()), ?FAULT_RATE(1,40,keypair())},
      begin
         case v_iodata(Msg) andalso keypair_valid(PK1, SK1) of
           true ->
             CT = enacl:box_seal(Msg, PK1),
             Err = enacl:box_seal_open([<<"x">>, CT], PK1, SK1),
             equals(Err, {error, failed_verification});
           false ->
             case box_seal(Msg, PK1) of
                 badarg -> true;
                 Res ->
                    failure(box_seal_open(Res, PK1, SK1))
            end
        end
    end).

prop_seal_box_correct() ->
    ?FORALL({Msg, {PK1, SK1}},
        {?FAULT_RATE(1, 40, g_iodata()),
         ?FAULT_RATE(1, 40, keypair())},
     begin
         case v_iodata(Msg) andalso keypair_valid(PK1, SK1) of
             true ->
                 SealedCipherText = enacl:box_seal(Msg, PK1),
                 {ok, DecodedMsg} = enacl:box_seal_open(SealedCipherText, PK1, SK1),
                 equals(iolist_to_binary(Msg), DecodedMsg);
             false ->
                case box_seal(Msg, PK1) of
                    badarg -> true;
                    Res -> failure(box_seal_open(Res, PK1, SK1))
                end
         end
     end).


%% CRYPTO SECRET BOX
%% ------------------------------------------------------------
%% * secretbox/3
%% * secretbo_open/3
secret_key_good() ->
	Sz = enacl:secretbox_KEYBYTES(),
	binary(Sz).

secret_key_bad() ->
	oneof([return(a),
	       nat(),
	       ?SUCHTHAT(B, binary(), byte_size(B) /= enacl:secretbox_KEYBYTES())]).

secret_key() ->
	?FAULT(secret_key_bad(), secret_key_good()).

secret_key_valid(SK) when is_binary(SK) ->
	Sz = enacl:secretbox_KEYBYTES(),
	byte_size(SK) == Sz;
secret_key_valid(_SK) -> false.

secretbox(Msg, Nonce, Key) ->
  try enacl:secretbox(Msg, Nonce, Key)
  catch error:badarg -> badarg
  end.

secretbox_open(Msg, Nonce, Key) ->
  try enacl:secretbox_open(Msg, Nonce, Key)
  catch error:badarg -> badarg
  end.

prop_secretbox_correct() ->
    ?FORALL({Msg, Nonce, Key},
            {?FAULT_RATE(1, 40, g_iodata()),
             ?FAULT_RATE(1, 40, nonce()),
             ?FAULT_RATE(1, 40, secret_key())},
      begin
        case v_iodata(Msg) andalso nonce_valid(Nonce) andalso secret_key_valid(Key) of
          true ->
             CipherText = enacl:secretbox(Msg, Nonce, Key),
             {ok, DecodedMsg} = enacl:secretbox_open(CipherText, Nonce, Key),
             equals(iolist_to_binary(Msg), DecodedMsg);
          false ->
             case secretbox(Msg, Nonce, Key) of
               badarg -> true;
               Res ->
                 failure(secretbox_open(Res, Nonce, Key))
             end
        end
      end).

prop_secretbox_failure_integrity() ->
    ?FORALL({Msg, Nonce, Key}, {g_iodata(), nonce(), secret_key()},
      begin
        CipherText = enacl:secretbox(Msg, Nonce, Key),
        Err = enacl:secretbox_open([<<"x">>, CipherText], Nonce, Key),
        equals(Err, {error, failed_verification})
      end).

%% AEAD ChaCha20Poly1305
%% ------------------------------------------------------------
%% * aead_chacha20poly1305_encrypt/4,
%% * aead_chacha20poly1305_decrypt/4,
prop_aead_chacha20poly1305_ietf() ->
  NPubBytes = enacl:aead_chacha20poly1305_ietf_NPUBBYTES(),
  ?FORALL({Key, Msg, AD, Nonce},
          {binary(32), binary(), ?LET(ADBytes, choose(0,16), binary(ADBytes)), binary(NPubBytes)},
  begin
    EncryptMsg = enacl:aead_chacha20poly1305_ietf_encrypt(Msg, AD, Nonce, Key),
    equals(enacl:aead_chacha20poly1305_ietf_decrypt(EncryptMsg, AD, Nonce, Key), Msg)
  end).

prop_aead_chacha20poly1305_ietf_fail() ->
  NPubBytes = enacl:aead_chacha20poly1305_ietf_NPUBBYTES(),
  ?FORALL({Key, Msg, AD, Nonce},
          {binary(32), binary(), ?LET(ADBytes, choose(0,16), binary(ADBytes)), binary(NPubBytes)},
  begin
    EncryptMsg = enacl:aead_chacha20poly1305_ietf_encrypt(Msg, AD, Nonce, Key),
    case enacl:aead_chacha20poly1305_ietf_decrypt(<<0:8, EncryptMsg/binary>>, AD, Nonce, Key) of
        {error, _} -> true;
        _          -> false
    end
  end).

%% * aead_xchacha20poly1305_encrypt/4,
%% * aead_xchacha20poly1305_decrypt/4,
prop_aead_xchacha20poly1305_ietf() ->
  NPubBytes = enacl:aead_xchacha20poly1305_ietf_NPUBBYTES(),
  ?FORALL({Key, Msg, AD, Nonce},
          {binary(32), binary(), ?LET(ADBytes, choose(0,16), binary(ADBytes)), binary(NPubBytes)},
  begin
    EncryptMsg = enacl:aead_xchacha20poly1305_ietf_encrypt(Msg, AD, Nonce, Key),
    equals(enacl:aead_xchacha20poly1305_ietf_decrypt(EncryptMsg, AD, Nonce, Key), Msg)
  end).

prop_aead_xchacha20poly1305_ietf_fail() ->
  NPubBytes = enacl:aead_xchacha20poly1305_ietf_NPUBBYTES(),
  ?FORALL({Key, Msg, AD, Nonce},
          {binary(32), binary(), ?LET(ADBytes, choose(0,16), binary(ADBytes)), binary(NPubBytes)},
  begin
    EncryptMsg = enacl:aead_xchacha20poly1305_ietf_encrypt(Msg, AD, Nonce, Key),
    case enacl:aead_xchacha20poly1305_ietf_decrypt(<<0:8, EncryptMsg/binary>>, AD, Nonce, Key) of
        {error, _} -> true;
        _          -> false
    end
  end).

%% CRYPTO STREAM
%% ------------------------------------------------------------
%% * stream/3
prop_stream_correct() ->
    ?FORALL({Len, Nonce, Key},
            {int(),
             ?FAULT_RATE(1, 40, nonce()),
             ?FAULT_RATE(1, 40, secret_key())},
        case Len >= 0 andalso nonce_valid(Nonce) andalso secret_key_valid(Key) of
          true ->
              CipherStream = enacl:stream(Len, Nonce, Key),
              equals(Len, byte_size(CipherStream));
          false ->
              badargs(fun() -> enacl:stream(Len, Nonce, Key) end)
        end).

xor_bytes(<<A, As/binary>>, <<B, Bs/binary>>) ->
    [A bxor B | xor_bytes(As, Bs)];
xor_bytes(<<>>, <<>>) -> [].

positive() ->
  ?LET(N, nat(), N+1).

chacha20_nonce() ->
  Sz = enacl:stream_chacha20_NONCEBYTES(),
  binary(Sz).

chacha20_key() ->
  Sz = enacl:stream_chacha20_KEYBYTES(),
  binary(Sz).

prop_stream_chacha20_correct() ->
  ?FORALL(Len, positive(),
    ?FORALL({Msg, Nonce, Key}, {binary(Len), chacha20_nonce(), chacha20_key()},
      begin
        CT = enacl:stream_chacha20_xor(Msg, Nonce, Key),
        Stream = enacl:stream_chacha20(Len, Nonce, Key),
        CT2 = list_to_binary(xor_bytes(Stream, Msg)),
        equals(CT, CT2)
      end)).

%% CRYPTO AUTH
%% ------------------------------------------------------------
%% * auth/2
%% * auth_verify/3
prop_auth_correct() ->
    ?FORALL({Msg, Key},
            {?FAULT_RATE(1, 40, g_iodata()),
             ?FAULT_RATE(1, 40, secret_key())},
       case v_iodata(Msg) andalso secret_key_valid(Key) of
         true ->
           Authenticator = enacl:auth(Msg, Key),
           equals(Authenticator, enacl:auth(Msg, Key));
         false ->
           badargs(fun() -> enacl:auth(Msg, Key) end)
       end).

authenticator_bad() ->
    oneof([a, int(), ?SUCHTHAT(X, binary(), byte_size(X) /= enacl:auth_BYTES())]).

authenticator_good(Msg, Key) when is_binary(Key) ->
    Sz = enacl:secretbox_KEYBYTES(),
    case v_iodata(Msg) andalso byte_size(Key) == Sz of
      true ->
        frequency([{1, ?LAZY({invalid, binary(enacl:auth_BYTES())})},
                   {3, return({valid, enacl:auth(Msg, Key)})}]);
      false ->
        binary(enacl:auth_BYTES())
    end;
authenticator_good(_Msg, _Key) ->
    binary(enacl:auth_BYTES()).

authenticator(Msg, Key) ->
  ?FAULT(authenticator_bad(), authenticator_good(Msg, Key)).

authenticator_valid({valid, _}) -> true;
authenticator_valid({invalid, _}) -> true;
authenticator_valid(_) -> false.

prop_auth_verify_correct() ->
    ?FORALL({Msg, Key},
            {?FAULT_RATE(1, 40, g_iodata()),
             ?FAULT_RATE(1, 40, secret_key())},
      ?FORALL(Authenticator, authenticator(Msg, Key),
        case v_iodata(Msg) andalso secret_key_valid(Key) andalso authenticator_valid(Authenticator) of
          true ->
            case Authenticator of
              {valid, A} ->
                equals(true, enacl:auth_verify(A, Msg, Key));
              {invalid, A} ->
                equals(false, enacl:auth_verify(A, Msg, Key))
            end;
          false ->
            badargs(fun() -> enacl:auth_verify(Authenticator, Msg, Key) end)
        end)).

%% CRYPTO ONETIME AUTH
%% ------------------------------------------------------------
%% * onetime_auth/2
%% * onetime_auth_verify/3
prop_onetimeauth_correct() ->
    ?FORALL({Msg, Key},
            {?FAULT_RATE(1, 40, g_iodata()),
             ?FAULT_RATE(1, 40, secret_key())},
       case v_iodata(Msg) andalso secret_key_valid(Key) of
         true ->
           Authenticator = enacl:onetime_auth(Msg, Key),
           equals(Authenticator, enacl:onetime_auth(Msg, Key));
         false ->
           badargs(fun() -> enacl:onetime_auth(Msg, Key) end)
       end).

ot_authenticator_bad() ->
    oneof([a, int(), ?SUCHTHAT(X, binary(), byte_size(X) /= enacl:onetime_auth_BYTES())]).

ot_authenticator_good(Msg, Key) when is_binary(Key) ->
    Sz = enacl:secretbox_KEYBYTES(),
    case v_iodata(Msg) andalso byte_size(Key) == Sz of
      true ->
        frequency([{1, ?LAZY({invalid, binary(enacl:onetime_auth_BYTES())})},
                   {3, return({valid, enacl:onetime_auth(Msg, Key)})}]);
      false ->
        binary(enacl:onetime_auth_BYTES())
    end;
ot_authenticator_good(_Msg, _Key) ->
    binary(enacl:auth_BYTES()).

ot_authenticator(Msg, Key) ->
  ?FAULT(ot_authenticator_bad(), ot_authenticator_good(Msg, Key)).

ot_authenticator_valid({valid, _}) -> true;
ot_authenticator_valid({invalid, _}) -> true;
ot_authenticator_valid(_) -> false.

prop_onetime_auth_verify_correct() ->
    ?FORALL({Msg, Key},
            {?FAULT_RATE(1, 40, g_iodata()),
             ?FAULT_RATE(1, 40, secret_key())},
      ?FORALL(Authenticator, ot_authenticator(Msg, Key),
        case v_iodata(Msg) andalso secret_key_valid(Key) andalso ot_authenticator_valid(Authenticator) of
          true ->
            case Authenticator of
              {valid, A} ->
                equals(true, enacl:onetime_auth_verify(A, Msg, Key));
              {invalid, A} ->
                equals(false, enacl:onetime_auth_verify(A, Msg, Key))
            end;
          false ->
            badargs(fun() -> enacl:onetime_auth_verify(Authenticator, Msg, Key) end)
        end)).

%% PWHASH
%% -------------------------------
%% * pwhash/2
%% * pwhash_str/1
%% * pwhash_str_verify/2
pwhash(Passwd, Salt) ->
  try
    enacl:pwhash(Passwd, Salt)
  catch
    error:badarg -> badarg
  end.

pwhash(Password, Salt, Ops, Mem, Alg) ->
  try
    enacl:pwhsah(Password, Salt, Ops, Mem, Alg)
  catch
    error:badarg -> badarg
  end.

pwhash_str(Passwd) ->
  try
    enacl:pwhash_str(Passwd)
  catch
    error:badarg -> badarg
  end.

pwhash_str_verify(PasswdHash, Passwd) ->
  try
    enacl:pwhash_str_verify(PasswdHash, Passwd)
  catch
    error:badarg -> badarg
  end.

prop_pwhash() ->
  ?FORALL({Password, Salt, OLimit, MLimit, Alg},
          {binary(16),
           binary(16),
           elements([interactive, moderate]), %% These could add senstitive, but are too runtime-expensive
           elements([interactive, moderate]), %% And that is for a reason.
           elements([default, 'argon2id13'])}, %% Argon2I13 uses different limits, so it is kept out as
                                               %% this would otherwise fail
    begin
       Bin1 = enacl:pwhash(Password, Salt, OLimit, MLimit, Alg),
       Bin2 = enacl:pwhash(Password, Salt, OLimit, MLimit, Alg),
       equals(Bin1, Bin2)
    end).

prop_pwhash_str_verify() ->
    ?FORALL({Passwd, OLimit, MLimit},
            {?FAULT_RATE(1, 40, g_iodata()),
             elements([interactive, moderate]),
             elements([interactive, moderate])},
            begin
                case v_iodata(Passwd) of
                    true ->
                        Ascii = enacl:pwhash_str(Passwd, OLimit, MLimit),
                        S = enacl:pwhash_str_verify(Ascii, Passwd),
                        equals(S, true);
                    false ->
                        badargs(fun() -> enacl:pwhash_str(Passwd) end),
                        badargs(fun() -> enacl:pwhash_str_verify("", Passwd) end)
                end
            end).

%% SUBTLE HASHING
%% ---------------------------
diff_pair() ->
    ?SUCHTHAT({X, Y}, {g_iodata(), g_iodata()},
        iolist_to_binary(X) /= iolist_to_binary(Y)).

prop_crypto_hash_eq() ->
    ?FORALL(X, g_iodata(),
        case v_iodata(X) of
          true -> equals(enacl:hash(X), enacl:hash(X));
          false ->
            try
              enacl:hash(X),
              false
            catch
              error:badarg -> true
            end
        end
    ).

prop_crypto_hash_neq() ->
    ?FORALL({X, Y}, diff_pair(),
        enacl:hash(X) /= enacl:hash(Y)
    ).

prop_crypto_shorthash_eq() ->
  ?FORALL(X, g_iodata(),
    case v_iodata(X) of
      true -> equals(enacl:hash(X), enacl:hash(X));
      false ->
        try
          enacl:hash(X),
          false
        catch
          error:badarg -> true
        end
      end
    ).
prop_crypto_generichash_eq() ->
  ?FORALL({Sz, X, Key}, {g_generichash_size(), g_generichash_data(), g_generichash_key()},
      equals(enacl:generichash(Sz, X, Key), enacl:generichash(Sz, X, Key))).

generichash_loop(S, []) -> S;
generichash_loop(S, [M|Ms]) ->
  S2 = enacl:generichash_update(S, M),
  generichash_loop(S2, Ms).

prop_crypto_generichash_multi_part_eq() ->
  ?FORALL({Sz, Xs, Key}, {g_generichash_size(), list(g_generichash_data()), g_generichash_key()},
  begin
    S1 = generichash_loop(enacl:generichash_init(Sz, Key), Xs),
    S2 = generichash_loop(enacl:generichash_init(Sz, Key), Xs),
    equals(enacl:generichash_final(S1), enacl:generichash_final(S2))
  end).

prop_crypto_shorthash_neq() ->
  ?FORALL({X, Y}, diff_pair(),
    enacl:hash(X) /= enacl:hash(Y)
  ).

%% STRING COMPARISON
%% -------------------------
%% * verify_16/2,
%% * verify_32/2
verify_pair_bad(Sz) ->
  ?LET(X, elements([fst, snd]),
    case X of
      fst ->
        {?SUCHTHAT(B, binary(), byte_size(B) /= Sz), binary(Sz)};
      snd ->
        {binary(Sz), ?SUCHTHAT(B, binary(), byte_size(B) /= Sz)}
    end).

verify_pair_good(Sz) ->
  oneof([
    ?LET(Bin, binary(Sz), {Bin, Bin}),
    ?SUCHTHAT({X, Y}, {binary(Sz), binary(Sz)}, X /= Y)]).

verify_pair(Sz) ->
  ?FAULT(verify_pair_bad(Sz), verify_pair_good(Sz)).

verify_pair_valid(Sz, X, Y) ->
    byte_size(X) == Sz andalso byte_size(Y) == Sz.

prop_verify_16() ->
    ?FORALL({X, Y}, verify_pair(16),
      case verify_pair_valid(16, X, Y) of
          true ->
              equals(X == Y, enacl:verify_16(X, Y));
          false ->
              try
                 enacl:verify_16(X, Y),
                 false
              catch
                  error:badarg -> true
              end
      end).

prop_verify_32() ->
    ?FORALL({X, Y}, verify_pair(32),
      case verify_pair_valid(32, X, Y) of
          true ->
              equals(X == Y, enacl:verify_32(X, Y));
          false ->
              try
                 enacl:verify_32(X, Y),
                 false
              catch
                  error:badarg -> true
              end
      end).

%% RANDOMBYTES
%% ------------------------------------------------------------
%% * randombytes/1
prop_randombytes() ->
    ?FORALL(X, g_nat(),
        case is_nat(X) of
            true ->
              R = enacl:randombytes(X),
              is_binary(R) andalso (byte_size(R) == X);
            false ->
                try
                    enacl:randombytes(X),
                    false
                catch
                    error:badarg ->
                       true
                end
       end).

prop_randombytes_uint32() ->
  ?FORALL(_, return(x),
    begin
      V = enacl:randombytes_uint32(),
      is_integer(V)
    end).

%% KX
%% ---------------------------
prop_kx() ->
  ?FORALL({{CPK, CSK}, {SPK, SSK}}, {kx_keypair_good(), kx_keypair_good()},
  begin
    #{ client_tx := CTX, client_rx := CRX} = enacl:kx_client_session_keys(CPK, CSK, SPK),
    #{ server_tx := STX, server_rx := SRX} = enacl:kx_server_session_keys(SPK, SSK, CPK),
    %% This keypair must be shared in both directions
    conjunction([{ctx_srx, equals(CTX, SRX)}, {stx_crx, equals(STX, CRX)}])
  end).

%% SCRAMBLING
prop_scramble_block() ->
    ?FORALL({Block, Key}, {binary(16), eqc_gen:largebinary(32)},
        is_binary(enacl_ext:scramble_block_16(Block, Key))).

%% Scala multiplication
prop_scalarmult() ->
  Bytes = 32,
  ?FORALL({S1, S2, Basepoint}, {binary(Bytes), binary(Bytes), binary(Bytes)},
          equals(enacl:curve25519_scalarmult(S1,
                       enacl:curve25519_scalarmult(S2, Basepoint)),
                 enacl:curve25519_scalarmult(S2,
                       enacl:curve25519_scalarmult(S1, Basepoint)))
         ).

%% Secretstream
secretstream_key() ->
  ?LET(K, enacl:secretstream_xchacha20poly1305_keygen(), K).

secretstream_msg() ->
  ?LET({Tag, AD, Msg}, {oneof([message,rekey,push]), binary(), binary()},
    {Tag, AD, Msg}).

secretstream_msgs() ->
  ?LET({Ms, {_, AD, Msg}}, {list(secretstream_msg()), secretstream_msg()},
   Ms ++ [{final, AD, Msg}]).

push_messages(_State, []) ->
  [];
push_messages(State, [{Tag, AD, Msg}|Next]) ->
  Block = enacl:secretstream_xchacha20poly1305_push(State, Msg, AD, Tag),
  [Block|push_messages(State, Next)].

pull_messages(_State, [], []) ->
  true;
pull_messages(State, [B|Bs], [{_Tag, AD, _Msg}=Expect|Next]) ->
  {Msgx, Tagx} = enacl:secretstream_xchacha20poly1305_pull(State, B, AD),
  case equals(Expect, {Tagx, AD, Msgx}) of
    true ->
      pull_messages(State, Bs, Next);
    R ->
      R
  end.

prop_secretstream() ->
  ?FORALL({Key, Msgs}, {secretstream_key(), secretstream_msgs()},
    begin
      %% Encrypt
      {Header, State} = enacl:secretstream_xchacha20poly1305_init_push(Key),
      Blocks = push_messages(State, Msgs),
      %% Decrypt & Verify
      DState = enacl:secretstream_xchacha20poly1305_init_pull(Header, Key),
      pull_messages(DState, Blocks, Msgs)
    end).

%% HELPERS

%% INTERNAL FUNCTIONS
%% ------------------------------------------------------------

badargs(Thunk) ->
  try
    Thunk(),
    false
  catch
    error:badarg -> true
  end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Joel Test Blobs

test_basic_signing() ->
  #{ public := PK0, secret := SK0 } = enacl:sign_keypair(),
  #{ public := PK1, secret := SK1 } = enacl:sign_keypair(),
  MSG0 = <<"This is super s3Kr3t, srsly!">>,
  [
    %% (+) Sign and open using valid keypair
    case enacl:sign_open(enacl:sign(MSG0, SK0), PK0) of
        {ok,MSG1} -> MSG0==MSG1;
        _         -> false
    end
  , %% (-) Sign and open using invalid keypair
    case enacl:sign_open(enacl:sign(MSG0, SK0), PK1) of
        {error,failed_verification} -> true;
        _                           -> false
    end
  , %% (+) Detached mode sig and verify
    { enacl:sign_verify_detached(enacl:sign_detached(MSG0, SK0), MSG0, PK0)
    , enacl:sign_verify_detached(enacl:sign_detached(MSG0, SK1), MSG0, PK1)
    }
  , %% (-) Incorrect sigs/PKs/messages given during verify
    { false == enacl:sign_verify_detached(enacl:sign_detached(MSG0, SK0), MSG0, PK1)
    , false == enacl:sign_verify_detached(enacl:sign_detached(MSG0, SK1), MSG0, PK0)
    , false == enacl:sign_verify_detached(enacl:sign_detached(MSG0, SK0), <<"bzzt">>, PK0)
    }
  ].


================================================
FILE: eqc_test/enacl_ext_eqc.erl
================================================
-module(enacl_ext_eqc).

-include_lib("eqc/include/eqc.hrl").
-compile({parse_transform, eqc_parallelize}).
-compile([export_all, nowarn_export_all]).

public_keypair() ->
    ?LET(#{ public := PK, secret := SK}, enacl_ext:curve25519_keypair(),
         {PK, SK}).

prop_public_key() ->
    ?FORALL({PK, SK}, public_keypair(),
        begin
            equals(PK, enacl_ext:curve25519_public_key(SK))
        end).

prop_shared_secret() ->
    ?FORALL([{PK1, SK1}, {PK2, SK2}],
            [public_keypair(), public_keypair()],
            begin
                Alice = enacl_ext:curve25519_shared(SK1, PK2),
                Bob = enacl_ext:curve25519_shared(SK2, PK1),
                equals(Alice, Bob)
            end).

prop_scramble_block() ->
    ?FORALL({Block, Key}, {binary(16), eqc_gen:largebinary(32)},
        is_binary(enacl_ext:scramble_block_16(Block, Key))).



================================================
FILE: priv/.dummy
================================================


================================================
FILE: rebar.config
================================================
{erl_opts, [debug_info]}.

{plugins, [pc]}.

{project_plugins, [rebar3_hex]}.

{provider_hooks, [
    {pre, [
        {compile, {pc, compile}},
        {clean,   {pc, clean}}
    ]}
]}.

{port_specs, [
    {"priv/enacl_nif.so", [
        "c_src/*.c"
    ]}
]}.

{port_env, [
    {"darwin", "CFLAGS", "$CFLAGS -fPIC -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes"},
    {"darwin", "CXXFLAGS", "$CXXFLAGS -fPIC -O3 -arch x86_64 -finline-functions -Wall"},
    {"darwin", "LDFLAGS", "$LDFLAGS -arch x86_64 -flat_namespace -undefined suppress -lsodium"},

    {"linux", "CFLAGS", "$CFLAGS -fPIC -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes"},
    {"linux", "CXXFLAGS", "$CXXFLAGS -fPIC -O3 -finline-functions -Wall"},
    {"linux", "LDFLAGS", "$LDFLAGS -lsodium"},

    {"freebsd", "CFLAGS", "$CFLAGS -fPIC -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes -I /usr/local/include"},
    {"freebsd", "CXXFLAGS", "$CXXFLAGS -fPIC -O3 -finline-functions -Wall"},
    {"freebsd", "LDFLAGS", "$LDFLAGS -fPIC -L /usr/local/lib -lsodium"},

    {"solaris", "CFLAGS", "$CFLAGS -fPIC -m64 -I/opt/local/include -O2 -std=c99 -finline-functions -Wall -Wmissing-prototypes"},
    {"solaris", "CXXFLAGS", "$CXXFLAGS -fPIC -O2 -finline-function -Wall"},
    {"solaris", "LDFLAGS", "$LDFLAGS -m64 -fPIC -L /opt/local/lib -lsodium"},

    {"win32", "CFLAGS", "$CFLAGS /LD /O2 /DNDEBUG"},
    {"win32", "LDFLAGS", "$LDFLAGS libsodium.dll.a"}
]}.


================================================
FILE: shell.nix
================================================
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = [
    pkgs.hello

    # keep this line if you use bash
    pkgs.bashInteractive
    pkgs.erlang
    pkgs.libsodium
  ];
}


================================================
FILE: src/Makefile
================================================
all:
	$(MAKE) -C .. compile


================================================
FILE: src/enacl.app.src
================================================
{application,enacl,
             [{description,"Erlang libsodium (NaCl) bindings"},
              {vsn,"1.2.1"},
              {registered,[]},
              {applications,[kernel,stdlib]},
              {env,[]},
              {maintainers,["Jesper Louis Andersen"]},
              {licenses,["MIT","ISC"]},
              {links,[{"Github","https://github.com/jlouis/enacl"}]}]}.


================================================
FILE: src/enacl.erl
================================================
%%% @doc Module enacl implements bindings to the NaCl/libsodium crypto-library
%%% <p>This module implements NIF bindings to the library known as NaCl (pronounced "salt").
%%% The NaCl library provides a sane cryptographic interface to the world in an attempt to
%%% make it harder to abuse and misuse cryptographic primitives.</p>
%%% <p>This module implements an Erlang-idiomatic API to the underlying library. If in doubt
%%% about a primitive, always consult the underlying documentation.</p>
%%% <p>There are two libraries in existence: NaCl and libsodium, the latter being a more
%%% portable variant of the NaCl library. The C-level API is interchangeable so we can run
%%% on any of these underlying libraries as seen from the Erlang world. We simply have to
%%% restrict ourselves to the portion of the code base which is overlapping.</p>
%%% <p><b>Warning:</b> It is necessary to apply the primitives here correctly. Wrong
%%% application may result in severely reduced strength of the cryptography. Take some
%%% time to make sure this is the case before using.</p>
%%% <p><b>Note:</b> All functions will fail with a `badarg' error if given incorrect
%%% parameters. Also, if something is wrong internally, they will raise an error of
%%% the form `enacl_internal_error'. There is usually no way to continue gracefully
%%% from either of these. A third error is `enacl_finalized', raised when you try
%%% re-using an already finalized state for multi-part messages.</p>
%%% @end.
-module(enacl).

%% Public key crypto
-export([
         %% EQC
         box_keypair/0,
         box/4,
         box_open/4,
         box_beforenm/2,
         box_afternm/3,
         box_open_afternm/3,
         box_NONCEBYTES/0,
         box_PUBLICKEYBYTES/0,
         box_SECRETKEYBYTES/0,
         box_BEFORENMBYTES/0,

         sign_PUBLICBYTES/0,
         sign_SECRETBYTES/0,
         sign_SEEDBYTES/0,
         sign_keypair/0,
         sign_seed_keypair/1,
         sign/2,
         sign_open/2,
         sign_detached/2,
         sign_verify_detached/3,

         sign_init/0,
         sign_update/2,
         sign_final_create/2,
         sign_final_verify/3,

         box_seal/2,
         box_seal_open/3
]).

%% Secret key crypto
-export([
         %% EQC
         secretbox_KEYBYTES/0,
         secretbox_NONCEBYTES/0,
         secretbox/3,
         secretbox_open/3,

         %% No Tests!
         stream_chacha20_KEYBYTES/0,
         stream_chacha20_NONCEBYTES/0,
         stream_chacha20/3,
         stream_chacha20_xor/3,

         %% EQC
         aead_chacha20poly1305_ietf_encrypt/4,
         aead_chacha20poly1305_ietf_decrypt/4,
         aead_chacha20poly1305_ietf_KEYBYTES/0,
         aead_chacha20poly1305_ietf_NPUBBYTES/0,
         aead_chacha20poly1305_ietf_ABYTES/0,
         aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX/0,

         aead_xchacha20poly1305_ietf_encrypt/4,
         aead_xchacha20poly1305_ietf_decrypt/4,
         aead_xchacha20poly1305_ietf_KEYBYTES/0,
         aead_xchacha20poly1305_ietf_NPUBBYTES/0,
         aead_xchacha20poly1305_ietf_ABYTES/0,
         aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX/0,

         %% EQC
         stream_KEYBYTES/0,
         stream_NONCEBYTES/0,
         stream/3,

         %% No Tests!
         stream_xor/3,

         %% EQC
         auth_KEYBYTES/0,
         auth_BYTES/0,
         auth/2,
         auth_verify/3,

         %% EQC
         onetime_auth_KEYBYTES/0,
         onetime_auth_BYTES/0,
         onetime_auth/2,
         onetime_auth_verify/3
]).

%% Hash functions
-export([
         %% No Tests!
         generichash/3,
         generichash/2,
         generichash_init/2,
         generichash_update/2,
         generichash_final/1,

         %% EQC!
         shorthash_key_size/0,
         shorthash_size/0,
         shorthash/2,

         pwhash_SALTBYTES/0,
         pwhash/2,
         pwhash/4,
         pwhash/5,
         pwhash_str/1,
         pwhash_str/3,
         pwhash_str_verify/2

]).

%% Key derivation
-export([
         kdf_KEYBYTES/0,
         kdf_CONTEXTBYTES/0,
         kdf_derive_from_key/3
]).

%% Low-level subtle functions which are hard to get correct
-export([
         %% EQC
         hash/1,
         verify_16/2,
         verify_32/2,

         %% No Tests!
         unsafe_memzero/1
]).

%% Randomness
-export([
         %% EQC
         randombytes/1,
         randombytes_uint32/0,
         randombytes_uniform/1
]).

%%% Specific primitives
%% Curve 25519 operations.
-export([
         %% No Tests!
         curve25519_scalarmult/1, curve25519_scalarmult/2,
         curve25519_scalarmult_base/1
]).

%% Ed 25519 operations.
-export([
         %% No Tests!
         crypto_sign_ed25519_keypair/0,
         crypto_sign_ed25519_sk_to_pk/1,
         crypto_sign_ed25519_public_to_curve25519/1,
         crypto_sign_ed25519_secret_to_curve25519/1,
         crypto_sign_ed25519_public_size/0,
         crypto_sign_ed25519_secret_size/0
        ]).

%% Key exchange functions
-export([
         %% EQC
         kx_keypair/0,
         kx_client_session_keys/3,
         kx_server_session_keys/3,
         kx_PUBLICKEYBYTES/0,
         kx_SECRETKEYBYTES/0,
         kx_SESSIONKEYBYTES/0
]).

%% Secretstream operations.
-export([
         %% Unit tests
         secretstream_xchacha20poly1305_ABYTES/0,
         secretstream_xchacha20poly1305_HEADERBYTES/0,
         secretstream_xchacha20poly1305_KEYBYTES/0,
         secretstream_xchacha20poly1305_MESSAGEBYTES_MAX/0,
         secretstream_xchacha20poly1305_TAG_MESSAGE/0,
         secretstream_xchacha20poly1305_TAG_PUSH/0,
         secretstream_xchacha20poly1305_TAG_REKEY/0,
         secretstream_xchacha20poly1305_TAG_FINAL/0,
         secretstream_xchacha20poly1305_keygen/0,
         secretstream_xchacha20poly1305_init_push/1,
         secretstream_xchacha20poly1305_push/4,
         secretstream_xchacha20poly1305_init_pull/2,
         secretstream_xchacha20poly1305_pull/3,
         secretstream_xchacha20poly1305_rekey/1
        ]).

%% Internal verification of the system
-export([verify/0]).

%% Type specifications
-type generichash_bytes() :: 10..64.
-type sign_state() :: reference().

-type pwhash_alg() :: default | argon2i13 | argon2id13 | pos_integer().
-type pwhash_limit() :: interactive | moderate | sensitive | pos_integer().
-type secretstream_xchacha20poly1305_tag() :: message | rekey | final | push | pos_integer().

-export_type([
    generichash_bytes/0,
    pwhash_alg/0,
    pwhash_limit/0,
    secretstream_xchacha20poly1305_tag/0,
    sign_state/0
]).



%% Definitions of system budgets
%% To get a grip for these, call `enacl_timing:all/0' on your system. The numbers here are
%% described in the README.md file.
-define(HASH_SIZE, 4 * 1024).
-define(HASH_REDUCTIONS, 17 * 2).
-define(BOX_BEFORENM_REDUCTIONS, 60).
-define(BOX_AFTERNM_SIZE, 8 * 1024).
-define(BOX_AFTERNM_REDUCTIONS, 17 * 2).
-define(SECRETBOX_SIZE, 8 * 1024).
-define(SECRETBOX_REDUCTIONS, 17 * 2).
-define(SECRETBOX_OPEN_REDUCTIONS, 17 * 2).
-define(STREAM_SIZE, 16 * 1024).
-define(STREAM_REDUCTIONS, 17 * 2).
-define(auth_BYTES, 4 * 1024).
-define(AUTH_REDUCTIONS, 17 * 2).
-define(ONETIME_auth_BYTES, 16 * 1024).
-define(ONETIME_AUTH_REDUCTIONS, 16 * 2).
-define(ED25519_PUBLIC_TO_CURVE_REDS, 20 * 2).
-define(ED25519_SECRET_TO_CURVE_REDS, 20 * 2).

%% Constants used throughout the code base
-define(CRYPTO_BOX_ZEROBYTES, 32).
-define(P_ZEROBYTES, <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>). %% 32 bytes of 0
-define(CRYPTO_BOX_BOXZEROBYTES, 16).
-define(P_BOXZEROBYTES, <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>).  %% 16 bytes

-define(CRYPTO_SECRETBOX_ZEROBYTES, 32).
-define(S_ZEROBYTES, <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>). %% 32 bytes
-define(CRYPTO_SECRETBOX_BOXZEROBYTES, 16).
-define(S_BOXZEROBYTES, <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>). %% 16 bytes
-define(CRYPTO_STREAM_CHACHA20_KEYBYTES, 32).
-define(CRYPTO_STREAM_CHACHA20_NONCEBYTES, 8).
-define(CRYPTO_STREAM_KEYBYTES, 32).
-define(CRYPTO_STREAM_NONCEBYTES, 24).
-define(CRYPTO_KX_PUBLICKEYBYTES, 32).
-define(CRYPTO_KX_SECRETKEYBYTES, 32).
-define(CRYPTO_KX_SESSIONKEYBYTES, 32).

-define(CRYPTO_GENERICHASH_BYTES_MIN, 16).
-define(CRYPTO_GENERICHASH_BYTES_MAX, 64).
-define(CRYPTO_GENERICHASH_BYTES, 32).
-define(CRYPTO_GENERICHASH_KEYBYTES_MIN, 16).
-define(CRYPTO_GENERICHASH_KEYBYTES_MAX, 64).
-define(CRYPTO_GENERICHASH_KEYBYTES, 32).

-define(CRYPTO_SECRETSTREAM_TAG_MESSAGE, 0).
-define(CRYPTO_SECRETSTREAM_TAG_PUSH, 1).
-define(CRYPTO_SECRETSTREAM_TAG_REKEY, 2).
-define(CRYPTO_SECRETSTREAM_TAG_FINAL, 3).

%% Size limits
-define(MAX_32BIT_INT, 1 bsl 32).

%% @doc Verify makes sure the constants defined in libsodium matches ours
verify() ->
    true = equals(binary:copy(<<0>>, enacl_nif:crypto_box_ZEROBYTES()), ?P_ZEROBYTES),
    true = equals(binary:copy(<<0>>, enacl_nif:crypto_box_BOXZEROBYTES()), ?P_BOXZEROBYTES),
    true = equals(binary:copy(<<0>>, enacl_nif:crypto_secretbox_ZEROBYTES()), ?S_ZEROBYTES),
    true = equals(binary:copy(<<0>>, enacl_nif:crypto_secretbox_BOXZEROBYTES()),
                  ?S_BOXZEROBYTES),

    Verifiers =
        [
         {crypto_stream
Download .txt
gitextract_mbe4rx4c/

├── .envrc
├── .github/
│   └── workflows/
│       ├── Makefile
│       ├── actions.cue
│       ├── ci.yaml
│       ├── setup.cue
│       └── steps.cue
├── .gitignore
├── .vscode/
│   ├── c_cpp_properties.json
│   └── settings.json
├── CHANGELOG.md
├── CONTRIBUTORS
├── LICENSE
├── Makefile
├── README.md
├── bench/
│   ├── enacl_nif.d
│   ├── funcall_enacl.d
│   └── timing.erl
├── benchmark.sh
├── c_src/
│   ├── aead.c
│   ├── aead.h
│   ├── enacl.c
│   ├── enacl.h
│   ├── enacl_ext.c
│   ├── enacl_ext.h
│   ├── enacl_nif.c
│   ├── generichash.c
│   ├── generichash.h
│   ├── hash.c
│   ├── hash.h
│   ├── kdf.c
│   ├── kdf.h
│   ├── kx.c
│   ├── kx.h
│   ├── public.c
│   ├── public.h
│   ├── pwhash.c
│   ├── pwhash.h
│   ├── randombytes.c
│   ├── randombytes.h
│   ├── secret.c
│   ├── secret.h
│   ├── secretstream.c
│   ├── secretstream.h
│   ├── sign.c
│   └── sign.h
├── doc/
│   └── overview.edoc
├── eqc_test/
│   ├── Makefile
│   ├── enacl_eqc.erl
│   └── enacl_ext_eqc.erl
├── priv/
│   └── .dummy
├── rebar.config
├── shell.nix
├── src/
│   ├── Makefile
│   ├── enacl.app.src
│   ├── enacl.erl
│   ├── enacl_ext.erl
│   └── enacl_nif.erl
└── test/
    └── enacl_SUITE.erl
Download .txt
SYMBOL INDEX (139 symbols across 14 files)

FILE: c_src/aead.c
  function ERL_NIF_TERM (line 11) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 17) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 23) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 29) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 36) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 78) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 128) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 134) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 140) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 146) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 153) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 196) | ERL_NIF_TERM

FILE: c_src/enacl.c
  function ERL_NIF_TERM (line 5) | ERL_NIF_TERM enacl_error_tuple(ErlNifEnv *env, char *error_atom) {
  function ERL_NIF_TERM (line 10) | ERL_NIF_TERM enacl_internal_error(ErlNifEnv *env) {
  function ERL_NIF_TERM (line 14) | ERL_NIF_TERM enacl_error_finalized(ErlNifEnv *env) {

FILE: c_src/enacl_ext.c
  function uint64_pack (line 8) | static void uint64_pack(unsigned char *y, ErlNifUInt64 x) {
  function ErlNifUInt64 (line 26) | static ErlNifUInt64 uint64_unpack(const unsigned char *x) {
  function crypto_block (line 47) | static int crypto_block(unsigned char *out, const unsigned char *in,
  function ERL_NIF_TERM (line 69) | ERL_NIF_TERM enif_scramble_block_16(ErlNifEnv *env, int argc,

FILE: c_src/enacl_nif.c
  function enacl_crypto_load (line 29) | static int enacl_crypto_load(ErlNifEnv *env, void **priv_data,
  function enacl_crypto_upgrade (line 51) | static int enacl_crypto_upgrade(ErlNifEnv* env, void **priv_data,
  function enacl_crypto_unload (line 57) | static int enacl_crypto_unload(ErlNifEnv* env, void **priv_data,
  function ERL_NIF_TERM (line 68) | static ERL_NIF_TERM enacl_crypto_verify_16(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 88) | static ERL_NIF_TERM enacl_crypto_verify_32(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 111) | static ERL_NIF_TERM enif_sodium_memzero(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 125) | static ERL_NIF_TERM
  function ERL_NIF_TERM (line 164) | static ERL_NIF_TERM

FILE: c_src/generichash.c
  type enacl_generichash_ctx (line 8) | typedef struct enacl_generichash_ctx {
  function enacl_init_generic_hash_ctx (line 21) | int enacl_init_generic_hash_ctx(ErlNifEnv *env) {
  function enacl_generic_hash_ctx_dtor (line 33) | static void enacl_generic_hash_ctx_dtor(ErlNifEnv *env,
  function ERL_NIF_TERM (line 51) | ERL_NIF_TERM enacl_crypto_generichash_BYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 56) | ERL_NIF_TERM enacl_crypto_generichash_BYTES_MIN(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 61) | ERL_NIF_TERM enacl_crypto_generichash_BYTES_MAX(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 66) | ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 71) | ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES_MIN(ErlNifEnv *env, int a...
  function ERL_NIF_TERM (line 76) | ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES_MAX(ErlNifEnv *env, int a...
  function ERL_NIF_TERM (line 81) | ERL_NIF_TERM enacl_crypto_generichash(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 137) | ERL_NIF_TERM enacl_crypto_generichash_init(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 220) | ERL_NIF_TERM enacl_crypto_generichash_update(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 259) | ERL_NIF_TERM enacl_crypto_generichash_final(ErlNifEnv *env, int argc,

FILE: c_src/hash.c
  function ERL_NIF_TERM (line 8) | ERL_NIF_TERM enacl_crypto_shorthash_BYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 13) | ERL_NIF_TERM enacl_crypto_shorthash_KEYBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 18) | ERL_NIF_TERM enacl_crypto_shorthash(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 40) | ERL_NIF_TERM enacl_crypto_hash(ErlNifEnv *env, int argc,

FILE: c_src/kdf.c
  function ERL_NIF_TERM (line 9) | ERL_NIF_TERM enacl_crypto_kdf_KEYBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 14) | ERL_NIF_TERM enacl_crypto_kdf_CONTEXTBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 20) | ERL_NIF_TERM enacl_crypto_kdf_derive_from_key(ErlNifEnv *env, int argc,

FILE: c_src/kx.c
  function ERL_NIF_TERM (line 10) | ERL_NIF_TERM enacl_crypto_kx_SECRETKEYBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 15) | ERL_NIF_TERM enacl_crypto_kx_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 20) | ERL_NIF_TERM enacl_crypto_kx_SESSIONKEYBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 25) | ERL_NIF_TERM enacl_crypto_kx_keypair(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 48) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 101) | ERL_NIF_TERM

FILE: c_src/public.c
  function ERL_NIF_TERM (line 9) | ERL_NIF_TERM enacl_crypto_box_NONCEBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 14) | ERL_NIF_TERM enacl_crypto_box_ZEROBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 19) | ERL_NIF_TERM enacl_crypto_box_BOXZEROBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 24) | ERL_NIF_TERM enacl_crypto_box_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 29) | ERL_NIF_TERM enacl_crypto_box_SECRETKEYBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 34) | ERL_NIF_TERM enacl_crypto_box_BEFORENMBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 39) | ERL_NIF_TERM enacl_crypto_box_SEALBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 44) | ERL_NIF_TERM enacl_crypto_box_keypair(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 67) | ERL_NIF_TERM enacl_crypto_box(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 117) | ERL_NIF_TERM enacl_crypto_box_open(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 157) | ERL_NIF_TERM enacl_crypto_box_beforenm(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 183) | ERL_NIF_TERM enacl_crypto_box_afternm(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 207) | ERL_NIF_TERM enacl_crypto_box_open_afternm(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 239) | ERL_NIF_TERM enacl_crypto_box_seal(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 257) | ERL_NIF_TERM enacl_crypto_box_seal_open(ErlNifEnv *env, int argc,

FILE: c_src/pwhash.c
  function enacl_pwhash_opslimit (line 8) | static size_t enacl_pwhash_opslimit(ErlNifEnv *env, ERL_NIF_TERM arg) {
  function enacl_pwhash_memlimit (line 34) | static size_t enacl_pwhash_memlimit(ErlNifEnv *env, ERL_NIF_TERM arg) {
  function ERL_NIF_TERM (line 60) | ERL_NIF_TERM enacl_crypto_pwhash_SALTBYTES(ErlNifEnv *env, int argc,
  function enacl_pwhash_alg (line 65) | static int enacl_pwhash_alg(ErlNifEnv *env, ERL_NIF_TERM arg) {
  function ERL_NIF_TERM (line 91) | ERL_NIF_TERM enacl_crypto_pwhash(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 132) | ERL_NIF_TERM enacl_crypto_pwhash_str(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 164) | ERL_NIF_TERM enacl_crypto_pwhash_str_verify(ErlNifEnv *env, int argc,

FILE: c_src/randombytes.c
  function ERL_NIF_TERM (line 8) | ERL_NIF_TERM enif_randombytes(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 26) | ERL_NIF_TERM enif_randombytes_uint32(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 38) | ERL_NIF_TERM enif_randombytes_uniform(ErlNifEnv *env, int argc,

FILE: c_src/secret.c
  function ERL_NIF_TERM (line 10) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 16) | ERL_NIF_TERM enacl_crypto_secretbox_KEYBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 21) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 27) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 33) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 39) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 45) | ERL_NIF_TERM enacl_crypto_stream_KEYBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 50) | ERL_NIF_TERM enacl_crypto_stream_NONCEBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 55) | ERL_NIF_TERM enacl_crypto_auth_BYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 60) | ERL_NIF_TERM enacl_crypto_auth_KEYBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 65) | ERL_NIF_TERM enacl_crypto_onetimeauth_BYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 70) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 76) | ERL_NIF_TERM enacl_crypto_secretbox(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 105) | ERL_NIF_TERM enacl_crypto_secretbox_open(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 140) | ERL_NIF_TERM enacl_crypto_stream_chacha20(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 165) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 190) | ERL_NIF_TERM enacl_crypto_stream(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 215) | ERL_NIF_TERM enacl_crypto_stream_xor(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 239) | ERL_NIF_TERM enacl_crypto_auth(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 261) | ERL_NIF_TERM enacl_crypto_auth_verify(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 282) | ERL_NIF_TERM enacl_crypto_onetimeauth(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 304) | ERL_NIF_TERM enacl_crypto_onetimeauth_verify(ErlNifEnv *env, int argc,

FILE: c_src/secretstream.c
  type enacl_secretstream_ctx (line 7) | typedef struct enacl_secretstream_ctx {
  function enacl_init_secretstream_ctx (line 19) | int enacl_init_secretstream_ctx(ErlNifEnv *env) {
  function enacl_secretstream_ctx_dtor (line 31) | static void enacl_secretstream_ctx_dtor(ErlNifEnv *env,
  function ERL_NIF_TERM (line 52) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 58) | ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_HEADERBYTES(
  function ERL_NIF_TERM (line 64) | ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_KEYBYTES(
  function ERL_NIF_TERM (line 69) | ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX(
  function ERL_NIF_TERM (line 75) | ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_MESSAGE(
  function ERL_NIF_TERM (line 82) | ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_PUSH(
  function ERL_NIF_TERM (line 88) | ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_REKEY(
  function ERL_NIF_TERM (line 94) | ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_FINAL(
  function ERL_NIF_TERM (line 100) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 126) | ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_init_push(
  function ERL_NIF_TERM (line 197) | ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_init_pull(
  function ERL_NIF_TERM (line 267) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 311) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 385) | ERL_NIF_TERM

FILE: c_src/sign.c
  type enacl_sign_ctx (line 8) | typedef struct enacl_sign_ctx {
  function enacl_init_sign_ctx (line 18) | int enacl_init_sign_ctx(ErlNifEnv *env) {
  function enacl_sign_ctx_dtor (line 30) | void enacl_sign_ctx_dtor(ErlNifEnv *env, enacl_sign_ctx *obj) {
  function ERL_NIF_TERM (line 49) | ERL_NIF_TERM enacl_crypto_sign_init(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 104) | ERL_NIF_TERM enacl_crypto_sign_update(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 139) | ERL_NIF_TERM enacl_crypto_sign_final_create(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 186) | ERL_NIF_TERM enacl_crypto_sign_final_verify(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 231) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 255) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 274) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 293) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 312) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 318) | ERL_NIF_TERM
  function ERL_NIF_TERM (line 324) | ERL_NIF_TERM enacl_crypto_sign_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 329) | ERL_NIF_TERM enacl_crypto_sign_SECRETKEYBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 334) | ERL_NIF_TERM enacl_crypto_sign_SEEDBYTES(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 339) | ERL_NIF_TERM enacl_crypto_sign_keypair(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 362) | ERL_NIF_TERM enacl_crypto_sign_seed_keypair(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 385) | ERL_NIF_TERM enacl_crypto_sign(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 408) | ERL_NIF_TERM enacl_crypto_sign_open(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 437) | ERL_NIF_TERM enacl_crypto_sign_detached(ErlNifEnv *env, int argc,
  function ERL_NIF_TERM (line 460) | ERL_NIF_TERM
Condensed preview — 58 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (280K chars).
[
  {
    "path": ".envrc",
    "chars": 23,
    "preview": "eval \"$(lorri direnv)\"\n"
  },
  {
    "path": ".github/workflows/Makefile",
    "chars": 39,
    "preview": "all:\n\tcue export --out yaml > ci.yaml\n\n"
  },
  {
    "path": ".github/workflows/actions.cue",
    "chars": 552,
    "preview": "package actions\n\n#Name: string\n#Branches: branches: [...string]\n#Tags: tags: [...string]\n\n#On: {\n\tpush?:         #Branch"
  },
  {
    "path": ".github/workflows/ci.yaml",
    "chars": 750,
    "preview": "name: build\non:\n  push:\n    branches:\n    - master\n  pull_request:\n    branches:\n    - master\njobs:\n  ci:\n    name: Run "
  },
  {
    "path": ".github/workflows/setup.cue",
    "chars": 336,
    "preview": "package actions\n\nname: #Name & \"build\"\non:   #On & {\n\tpush: branches: [\n\t\t_branch,\n\t]\n\tpull_request: branches: [\n\t\t_bran"
  },
  {
    "path": ".github/workflows/steps.cue",
    "chars": 742,
    "preview": "package actions\n\n// Versions for simplicity\n_versions: {\n\t// The versions here have an underlying Debian/Ubuntu which su"
  },
  {
    "path": ".gitignore",
    "chars": 192,
    "preview": ".rebar\n.rebar3\nebin\n*.beam\n*.o\n*.eqc\n*.so\neqc_test/.eqc-info\ndoc/edoc-info\ndoc/*.html\ndoc/*.png\ndoc/*.css\n_build\n/.eqc-i"
  },
  {
    "path": ".vscode/c_cpp_properties.json",
    "chars": 424,
    "preview": "{\n    \"configurations\": [\n        {\n            \"name\": \"Linux\",\n            \"includePath\": [\n                \"${workspa"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 42,
    "preview": "{\n    \"C_Cpp.errorSquiggles\": \"Disabled\"\n}"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 12157,
    "preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
  },
  {
    "path": "CONTRIBUTORS",
    "chars": 297,
    "preview": "List of people who have contributed to the eNaCl source code:\n\nAlexander Færøy\nAlexander Malaev\nAmir Ghassemi Nasr\nBryan"
  },
  {
    "path": "LICENSE",
    "chars": 1093,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2014-2018 Jesper Louis Andersen\n\nPermission is hereby granted, free of charge, to a"
  },
  {
    "path": "Makefile",
    "chars": 519,
    "preview": "REBAR=rebar3\nRUN_EQC=erl -pa _build/default/lib/enacl/ebin -noshell -s enacl_eqc -s init stop\n\n.PHONY: compile\ncompile:\n"
  },
  {
    "path": "README.md",
    "chars": 12930,
    "preview": "# Erlang bindings for NaCl/libsodium\n\nThis library provides bindings for the libsodium cryptographic library\nfor Erlang."
  },
  {
    "path": "bench/enacl_nif.d",
    "chars": 711,
    "preview": "/* Dirty NIF schedule overhead */\npid$target:beam.smp:schedule_dirty_cpu_nif:return\n{\n\ts = timestamp;\n}\n\npid$target:libs"
  },
  {
    "path": "bench/funcall_enacl.d",
    "chars": 216,
    "preview": "erlang*:::nif-entry\n{\n\tfuncall_entry_ts[cpu, copyinstr(arg1)] = timestamp;\n}\n\nerlang*:::nif-return\n{\n\t@time[cpu, copyins"
  },
  {
    "path": "bench/timing.erl",
    "chars": 3668,
    "preview": "-module(timing).\n-export([test/0]).\n\ntest() ->\n    randombytes(),\n    randombytes(),\n    randombytes(),\n    hash(),\n    "
  },
  {
    "path": "benchmark.sh",
    "chars": 293,
    "preview": "#!/bin/bash\n\ncase $1 in\n\t\"on\" )\n\t\tfor i in /sys/devices/system/cpu/cpu[0-7] ; do\n\t\t\techo performance > $i/cpufreq/scalin"
  },
  {
    "path": "c_src/aead.c",
    "chars": 7386,
    "preview": "#include <sodium.h>\n\n#include <erl_nif.h>\n\n#include \"aead.h\"\n#include \"enacl.h\"\n\n/*\n * AEAD ChaCha20 Poly1305\n */\nERL_NI"
  },
  {
    "path": "c_src/aead.h",
    "chars": 2040,
    "preview": "#ifndef ENACL_AEAD_H\n#define ENACL_AEAD_H\n\n#include <erl_nif.h>\n\n/* AEAD ChaCha20 Poly1305 */\nERL_NIF_TERM\nenacl_crypto_"
  },
  {
    "path": "c_src/enacl.c",
    "chars": 499,
    "preview": "#include <erl_nif.h>\n\n#include \"enacl.h\"\n\nERL_NIF_TERM enacl_error_tuple(ErlNifEnv *env, char *error_atom) {\n  return en"
  },
  {
    "path": "c_src/enacl.h",
    "chars": 314,
    "preview": "#ifndef ENACL_H\n#define ENACL_H\n\n#include <erl_nif.h>\n\n#define ATOM_OK \"ok\"\n#define ATOM_ERROR \"error\"\n#define ATOM_TRUE"
  },
  {
    "path": "c_src/enacl_ext.c",
    "chars": 1907,
    "preview": "#include <sodium.h>\n\n#include <erl_nif.h>\n\n#include \"enacl.h\"\n#include \"enacl_ext.h\"\n\nstatic void uint64_pack(unsigned c"
  },
  {
    "path": "c_src/enacl_ext.h",
    "chars": 197,
    "preview": "#ifndef ENACL_EXT_H\n#define ENACL_EXT_H\n\n#include <erl_nif.h>\n\nERL_NIF_TERM enif_scramble_block_16(ErlNifEnv *env, int a"
  },
  {
    "path": "c_src/enacl_nif.c",
    "chars": 18578,
    "preview": "#include <sodium.h>\n#include <string.h>\n\n#include <erl_nif.h>\n\n#include \"aead.h\"\n#include \"enacl.h\"\n#include \"enacl_ext."
  },
  {
    "path": "c_src/generichash.c",
    "chars": 7950,
    "preview": "#include <sodium.h>\n\n#include <erl_nif.h>\n\n#include \"enacl.h\"\n#include \"generichash.h\"\n\ntypedef struct enacl_generichash"
  },
  {
    "path": "c_src/generichash.h",
    "chars": 1597,
    "preview": "#ifndef ENACL_GENERICHASH_H\n#define ENACL_GENERICHASH_H\n\n#include <erl_nif.h>\n\nint enacl_init_generic_hash_ctx(ErlNifEnv"
  },
  {
    "path": "c_src/hash.c",
    "chars": 1632,
    "preview": "#include <sodium.h>\n\n#include <erl_nif.h>\n\n#include \"enacl.h\"\n#include \"hash.h\"\n\nERL_NIF_TERM enacl_crypto_shorthash_BYT"
  },
  {
    "path": "c_src/hash.h",
    "chars": 598,
    "preview": "#ifndef ENACL_HASH_H\n#define ENACL_HASH_H\n\n#include <erl_nif.h>\n\nERL_NIF_TERM enacl_crypto_shorthash_BYTES(ErlNifEnv *en"
  },
  {
    "path": "c_src/kdf.c",
    "chars": 1538,
    "preview": "#include <sodium.h>\n\n#include <erl_nif.h>\n\n#include \"enacl.h\"\n#include \"kdf.h\"\n\n\nERL_NIF_TERM enacl_crypto_kdf_KEYBYTES("
  },
  {
    "path": "c_src/kdf.h",
    "chars": 491,
    "preview": "#ifndef ENACL_KDF_H\n#define ENACL_KDF_H\n\n#include <erl_nif.h>\n\nERL_NIF_TERM enacl_crypto_kdf_KEYBYTES(ErlNifEnv *env, in"
  },
  {
    "path": "c_src/kx.c",
    "chars": 4243,
    "preview": "#include <sodium.h>\n\n#include <erl_nif.h>\n\n#include \"enacl.h\"\n#include \"kx.h\"\n\n/* Key exchange */\n\nERL_NIF_TERM enacl_cr"
  },
  {
    "path": "c_src/kx.h",
    "chars": 934,
    "preview": "#ifndef ENACL_KX_H\n#define ENACL_KX_H\n\n#include <erl_nif.h>\n\nERL_NIF_TERM enacl_crypto_kx_SECRETKEYBYTES(ErlNifEnv *env,"
  },
  {
    "path": "c_src/public.c",
    "chars": 9118,
    "preview": "#include <sodium.h>\n\n#include <erl_nif.h>\n\n#include \"enacl.h\"\n#include \"public.h\"\n\n/* Public-key cryptography */\nERL_NIF"
  },
  {
    "path": "c_src/public.h",
    "chars": 2070,
    "preview": "#ifndef ENACL_PUBLIC_H\n#define ENACL_PUBLIC_H\n\nERL_NIF_TERM enacl_crypto_box_NONCEBYTES(ErlNifEnv *env, int argc,\n      "
  },
  {
    "path": "c_src/pwhash.c",
    "chars": 4867,
    "preview": "#include <sodium.h>\n\n#include <erl_nif.h>\n\n#include \"enacl.h\"\n#include \"pwhash.h\"\n\nstatic size_t enacl_pwhash_opslimit(E"
  },
  {
    "path": "c_src/pwhash.h",
    "chars": 610,
    "preview": "#ifndef ENACL_PWHASH_H\n#define ENACL_PWHASH_H\n\n#include <erl_nif.h>\n\nERL_NIF_TERM enacl_crypto_pwhash_SALTBYTES(ErlNifEn"
  },
  {
    "path": "c_src/randombytes.c",
    "chars": 1190,
    "preview": "#include <sodium.h>\n\n#include <erl_nif.h>\n\n#include \"enacl.h\"\n#include \"randombytes.h\"\n\nERL_NIF_TERM enif_randombytes(Er"
  },
  {
    "path": "c_src/randombytes.h",
    "chars": 460,
    "preview": "#ifndef ENACL_RANDOMBYTES_H\n#define ENACL_RANDOMBYTES_H\n\n#include <erl_nif.h>\n\nERL_NIF_TERM enif_randombytes(ErlNifEnv *"
  },
  {
    "path": "c_src/secret.c",
    "chars": 10198,
    "preview": "#include <sodium.h>\n\n#include <erl_nif.h>\n\n#include \"enacl.h\"\n#include \"secret.h\"\n\n/* Secret key cryptography */\n\nERL_NI"
  },
  {
    "path": "c_src/secret.h",
    "chars": 3128,
    "preview": "#ifndef ENACL_SECRET_H\n#define ENACL_SECRET_H\n\nERL_NIF_TERM enacl_crypto_secretbox_NONCEBYTES(ErlNifEnv *env, int argc,\n"
  },
  {
    "path": "c_src/secretstream.c",
    "chars": 12525,
    "preview": "#include <erl_nif.h>\n#include <sodium.h>\n\n#include \"enacl.h\"\n#include \"secretstream.h\"\n\ntypedef struct enacl_secretstrea"
  },
  {
    "path": "c_src/secretstream.h",
    "chars": 2031,
    "preview": "#ifndef ENACL_SECRETSTREAM_H\n#define ENACL_SECRETSTREAM_H\n\n#include <erl_nif.h>\n\nint enacl_init_secretstream_ctx(ErlNifE"
  },
  {
    "path": "c_src/sign.c",
    "chars": 13190,
    "preview": "#include <sodium.h>\n\n#include <erl_nif.h>\n\n#include \"enacl.h\"\n#include \"sign.h\"\n\ntypedef struct enacl_sign_ctx {\n  ErlNi"
  },
  {
    "path": "c_src/sign.h",
    "chars": 2803,
    "preview": "#ifndef ENACL_SIGN_H\n#define ENACL_SIGN_H\n\n#include <erl_nif.h>\n\nint enacl_init_sign_ctx(ErlNifEnv *env);\n\nERL_NIF_TERM "
  },
  {
    "path": "doc/overview.edoc",
    "chars": 99,
    "preview": "@author Jesper Louis Andersen\n\n@title  enacl - Erlang bindings for the NaCl cryptographic library\n\n"
  },
  {
    "path": "eqc_test/Makefile",
    "chars": 101,
    "preview": "eqc_compile:\n\t$(MAKE) -C .. eqc_compile\n\nconsole:\n\terl -pa ../ebin ../deps/*/ebin\n\nclean:\n\trm *.beam\n"
  },
  {
    "path": "eqc_test/enacl_eqc.erl",
    "chars": 33857,
    "preview": "-module(enacl_eqc).\n-include_lib(\"eqc/include/eqc.hrl\").\n-compile([export_all, nowarn_export_all]).\n\n-ifndef(mini).\n-com"
  },
  {
    "path": "eqc_test/enacl_ext_eqc.erl",
    "chars": 876,
    "preview": "-module(enacl_ext_eqc).\n\n-include_lib(\"eqc/include/eqc.hrl\").\n-compile({parse_transform, eqc_parallelize}).\n-compile([ex"
  },
  {
    "path": "priv/.dummy",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "rebar.config",
    "chars": 1471,
    "preview": "{erl_opts, [debug_info]}.\n\n{plugins, [pc]}.\n\n{project_plugins, [rebar3_hex]}.\n\n{provider_hooks, [\n    {pre, [\n        {c"
  },
  {
    "path": "shell.nix",
    "chars": 186,
    "preview": "{ pkgs ? import <nixpkgs> {} }:\n\npkgs.mkShell {\n  buildInputs = [\n    pkgs.hello\n\n    # keep this line if you use bash\n "
  },
  {
    "path": "src/Makefile",
    "chars": 28,
    "preview": "all:\n\t$(MAKE) -C .. compile\n"
  },
  {
    "path": "src/enacl.app.src",
    "chars": 381,
    "preview": "{application,enacl,\n             [{description,\"Erlang libsodium (NaCl) bindings\"},\n              {vsn,\"1.2.1\"},\n       "
  },
  {
    "path": "src/enacl.erl",
    "chars": 58915,
    "preview": "%%% @doc Module enacl implements bindings to the NaCl/libsodium crypto-library\n%%% <p>This module implements NIF binding"
  },
  {
    "path": "src/enacl_ext.erl",
    "chars": 2429,
    "preview": "%%% @doc module enacl_ext implements various enacl extensions.\n%%% <p>None of the extensions listed here are part of the"
  },
  {
    "path": "src/enacl_nif.erl",
    "chars": 16428,
    "preview": "%%% @doc module enacl_nif provides the low-level interface to the NaCl/Sodium NIFs.\n%%% @end\n%%% @private\n-module(enacl_"
  },
  {
    "path": "test/enacl_SUITE.erl",
    "chars": 6935,
    "preview": "-module(enacl_SUITE).\n-include_lib(\"common_test/include/ct.hrl\").\n\n-compile([export_all, nowarn_export_all]).\n\nsuite() -"
  }
]

About this extraction

This page contains the full source code of the jlouis/enacl GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 58 files (262.5 KB), approximately 76.9k tokens, and a symbol index with 139 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!